vue工程目錄結構

主體結構

├─build          webpack的一些配置文件以及服務啟動文件
├─config         多為build中所依賴的文件
├─dist            
├─node_modules
├─src            頁面以及邏輯文件夾
│ ├─assets
│ ├─components
│ ├─router
│ ├─App.vue
│ ├─main.js
├─static         字體以及公眾樣式文件夾
├─.babelrc       es6編譯文件配置
├─.editorconfig  編寫風格配置文件,比如縮進文件格式
├─.eslintignore  忽略檢測一些文件格式
├─.eslintrc.js   代碼規(guī)范化配置文件
├─.gitignore     忽略上傳一些文件或配置
├─.postcssrc.js  用js來處理css
├─index.html     主文件入口
├─package-lock.json
├─package.json   npm依賴以及開發(fā)生產(chǎn)環(huán)境用的模塊包
└─README.md      解釋說明項目是做什么的

package.json

npm install命令根據(jù)這個配置文件增減來管理本地的安裝包兰伤。

//從name到private都是package的配置信息泄伪,也就是我們在腳手架搭建中輸入的項目描述
{
  //項目名稱:不能以.(點)或者_(下劃線)開頭,不能包含大寫字母于未,具有明確的的含義與現(xiàn)有項目名字不重復
  "name": "learn",
  "version": "1.0.0",  //項目版本號:遵循“大版本.次要版本.小版本”
  "description": "A Vue.js project",  //項目描述
  "author": "WSQ",  //作者名字
  "private": true,  //是否私有
  "scripts": {  //scripts中的子項即是我們在控制臺運行的腳本的縮寫
    //webpack-dev-server:啟動了http服務器鸯两,實現(xiàn)實時編譯;
    //inline模式會在webpack.config.js入口配置中新增webpack-dev-server/client?http://localhost:8080/的入口,
    //使得我們訪問路徑為localhost:8080/index.html(相應的還有另外一種模式Iframe);
    //progress:顯示打包的進度
    "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
    "start": "npm run dev",  //與npm run dev相同,直接運行開發(fā)環(huán)境
    "lint": "eslint --ext .js,.vue src",
    "build": "node build/build.js"  //使用node運行build文件
  },
  //dependencies(項目依賴庫):在安裝時使用--save則寫入到dependencies
  "dependencies": {
    "vue": "^2.5.2",
    "vue-router": "^3.0.1"
  },
  //devDependencies(開發(fā)依賴庫):在安裝時使用--save-dev將寫入到devDependencies
  "devDependencies": {
    //autoprefixer作為postcss插件用來解析CSS補充前綴窃这,
    //例如display:flex會補充為display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex
    "autoprefixer": "^7.1.2",
    //babel:以下幾個babel開頭的都是針對es6解析的插件。用最新標準編寫的JavaScript代碼向下編譯成可以在今天隨處可用的版本
    "babel-core": "^6.22.1",  //babel的核心征候,把js代碼分析成ast 杭攻,方便各個插件分析語法進行相應的處理
    "babel-eslint": "^8.2.1",
    "babel-helper-vue-jsx-merge-props": "^2.0.3",  //預制babel-template函數(shù),提供給vue,jsx等使用
    "babel-loader": "^7.1.1",  //使項目運行使用Babel和webpack來傳輸js文件疤坝,使用babel-core提供的api進行轉譯
    "babel-plugin-syntax-jsx": "^6.18.0",  //支持jsx
    "babel-plugin-transform-runtime": "^6.22.0",  //避免編譯輸出中的重復兆解,直接編譯到build環(huán)境中
    "babel-plugin-transform-vue-jsx": "^3.5.0",  //babel轉譯過程中使用到的插件,避免重復
    "babel-preset-env": "^1.3.2",  //轉為es5跑揉,transform階段使用到的插件之一
    "babel-preset-stage-2": "^6.22.0",  //ECMAScript第二階段的規(guī)范
    "chalk": "^2.0.1",  //用來在命令行輸出不同顏色文字
    "copy-webpack-plugin": "^4.0.1",  //拷貝資源和文件
    //webpack先用css-loader加載器去解析后綴為css的文件锅睛,再使用style-loader生成一個內(nèi)容為最終解析完的css代碼的style標簽,放到head標簽里
    "css-loader": "^0.28.0",
    "eslint": "^4.15.0",
    "eslint-config-standard": "^10.2.1",
    "eslint-friendly-formatter": "^3.0.0",
    "eslint-loader": "^1.7.1",
    "eslint-plugin-import": "^2.7.0",
    "eslint-plugin-node": "^5.2.0",
    "eslint-plugin-promise": "^3.4.0",
    "eslint-plugin-standard": "^3.0.1",
    "eslint-plugin-vue": "^4.0.0",
    "extract-text-webpack-plugin": "^3.0.0",  //將一個以上的包里面的文本提取到單獨文件中
    "file-loader": "^1.1.4",  //打包壓縮文件历谍,與url-loader用法類似
    "friendly-errors-webpack-plugin": "^1.6.1",  //識別某些類別的webpack錯誤和清理现拒,聚合和優(yōu)先排序,以提供更好的開發(fā)經(jīng)驗
    "html-webpack-plugin": "^2.30.1",  //簡化了HTML文件的創(chuàng)建扮饶,引入了外部資源具练,創(chuàng)建html的入口文件,可通過此項進行多頁面的配置
    "node-notifier": "^5.1.2",  //支持使用node發(fā)送跨平臺的本地通知
    //壓縮提取出的css甜无,并解決ExtractTextPlugin分離出的js重復問題(多個文件引入同一css文件)
    "optimize-css-assets-webpack-plugin": "^3.2.0",  
    "ora": "^1.2.0",  //加載(loading)的插件
    "portfinder": "^1.0.13",  //查看進程端口
    "postcss-import": "^11.0.0",  //可以消耗本地文件、節(jié)點模塊或web_modules
    "postcss-loader": "^2.0.8",  //用來兼容css的插件
    "postcss-url": "^7.2.1",  //URL上重新定位哥遮、內(nèi)聯(lián)或復制
    "rimraf": "^2.6.0",  //節(jié)點的UNIX命令RM—RF,強制刪除文件或者目錄的命令
    "semver": "^5.3.0",  //用來對特定的版本號做判斷的
    "shelljs": "^0.7.6",  //使用它來消除shell腳本在UNIX上的依賴性岂丘,同時仍然保留其熟悉和強大的命令,即可執(zhí)行Unix系統(tǒng)命令
    "uglifyjs-webpack-plugin": "^1.1.1",  //壓縮js文件
    "url-loader": "^0.5.8",  //壓縮文件眠饮,可將圖片轉化為base64
    "vue-loader": "^13.3.0",  //VUE單文件組件的webpack加載器
    "vue-style-loader": "^3.0.1",  //類似于樣式加載程序奥帘,您可以在CSS加載器之后將其鏈接,以將CSS動態(tài)地注入到文檔中作為樣式標簽
    "vue-template-compiler": "^2.5.2",  //這個包可以用來預編譯VUE模板到渲染函數(shù)仪召,以避免運行時編譯開銷和CSP限制
    "webpack": "^3.6.0",  //打包工具
    "webpack-bundle-analyzer": "^2.9.0",  //可視化webpack輸出文件的大小
    "webpack-dev-server": "^2.9.1",  //提供一個提供實時重載的開發(fā)服務器
    //它將數(shù)組和合并對象創(chuàng)建一個新對象寨蹋。如果遇到函數(shù)松蒜,它將執(zhí)行它們,通過算法運行結果已旧,然后再次將返回的值封裝在函數(shù)中
    "webpack-merge": "^4.1.0"
  },  
  //engines是引擎秸苗,指定node和npm版本
  "engines": {
    "node": ">= 6.0.0",
    "npm": ">= 3.0.0"
  },
  //限制了瀏覽器或者客戶端需要什么版本才可運行
  "browserslist": [
    "> 1%",
    "last 2 versions",
    "not ie <= 8"
  ]
}

devDependenciesdependencies的區(qū)別:devDependencies里面的插件只用于開發(fā)環(huán)境,不用于生產(chǎn)環(huán)境运褪,即輔助作用惊楼,打包的時候需要,打包完成就不需要了秸讹。而dependencies是需要發(fā)布到生產(chǎn)環(huán)境的檀咙,自始至終都在。比如wepack等只是在開發(fā)中使用的包就寫入到devDependencies璃诀,而像vue這種項目全程依賴的包要寫入到devDependencies弧可。
file-loaderurl-loader的區(qū)別:以圖片為例,file-loader可對圖片進行壓縮劣欢,但是還是通過文件路徑進行引入棕诵,當http請求增多時會降低頁面性能,而url-loader通過設定limit參數(shù)氧秘,小于limit字節(jié)的圖片會被轉成base64的文件年鸳,大于limit字節(jié)的將進行圖片壓縮的操作⊥柘啵總而言之搔确,url-loaderfile-loader的上層封裝。

.postcssrc.js

.postcssrc.js文件其實是postcss-loader包的一個配置灭忠,里面寫進去需要使用到的插件膳算。

module.exports = {
  "plugins": {
    "postcss-import": {},
    "postcss-url": {},
    "autoprefixer": {}
  }
}

.babelrc

該文件是es6解析的一個配置。

{
  //制定轉碼的規(guī)則
  "presets": [
    //env是使用babel-preset-env插件將js進行轉碼成es5弛作,
    //并且設置不轉碼的AMD,COMMONJS的模塊文件涕蜂,制定瀏覽器的兼容
    ["env", {
      "modules": false,
      "targets": {
        "browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
      }
    }],
    "stage-2"
  ],
  "plugins": ["transform-vue-jsx", "transform-runtime"]
}

src內(nèi)文件

我們開發(fā)的代碼都存放在src目錄下,根據(jù)需要我們通常會再建一些文件夾映琳。比如pages的文件夾机隙,用來存放頁面讓components文件夾專門做好組件的工作;api文件夾萨西,來封裝請求的參數(shù)和方法有鹿;store文件夾,使用vuex來作為vue的狀態(tài)管理工具谎脯,我也常叫它作前端的數(shù)據(jù)庫等葱跋。

  1. assets文件:腳手架自動回放入一個圖片在里面作為初始頁面的logo。平常我們使用的時候會在里面建立jscss娱俺,img稍味,fonts等文件夾,作為靜態(tài)資源調用荠卷。
  2. components文件夾:用來存放組件模庐,合理地使用組件可以高效地實現(xiàn)復用等功能,從而更好地開發(fā)項目僵朗。一般情況下比如創(chuàng)建頭部組件的時候赖欣,我們會新建一個header的文件夾,然后再新建一個header.vue的文件夾验庙。
  3. router文件夾:該文件夾下有一個叫index.js文件顶吮,用于實現(xiàn)頁面的路由跳轉。
  4. App.vue:作為我們的主組件粪薛,可通過使用開放入口讓其他的頁面組件得以顯示悴了。
  5. main.js:作為我們的入口文件,主要作用是初始化vue實例并使用需要的插件违寿,小型項目省略router時可放在該處湃交。

其他文件

  • .editorconfig:編輯器的配置文件
  • .gitignore:忽略git提交的一個文件,配置之后提交時將不會加載忽略的文件
  • index.html:頁面入口藤巢,經(jīng)過編譯之后的代碼將插入到這來搞莺。
  • package.lock.json:鎖定安裝時的包的版本號,并且需要上傳到git掂咒,以保證其他人在npm install時大家的依賴能保證一致
  • README.md:可此填寫項目介紹
  • node_modules:根據(jù)package.json安裝時候生成的的依賴(安裝包)

config文件夾

├─config 
│ ├─dev.env.js 
│ ├─index.js 
│ ├─prod.env.js 

config/dev.env.js

config內(nèi)的文件其實是服務于build的才沧,大部分是定義一個變量export出去。

'use strict'  //采用嚴格模式
const merge = require('webpack-merge')
const prodEnv = require('./prod.env')
//webpack-merge提供了一個合并函數(shù)绍刮,它將數(shù)組和合并對象創(chuàng)建一個新對象温圆。
//如果遇到函數(shù),它將執(zhí)行它們孩革,通過算法運行結果岁歉,然后再次將返回的值封裝在函數(shù)中.這邊將dev和prod進行合并
module.exports = merge(prodEnv, {
  NODE_ENV: '"development"'
})

config/prod.env.js

當開發(fā)是調取dev.env.js的開發(fā)環(huán)境配置,發(fā)布時調用prod.env.js的生產(chǎn)環(huán)境配置膝蜈。

'use strict'
module.exports = {
  NODE_ENV: '"production"'
}

config/index.js

'use strict'
// Template version: 1.3.1
const path = require('path')
module.exports = {
  dev: {  // 開發(fā)環(huán)境下面的配置
    // 路徑相關配置
    assetsSubDirectory: 'static',  //子目錄锅移,一般存放css,js,image等文件
    assetsPublicPath: '/',  //根目錄
    proxyTable: {},  //可利用該屬性解決跨域的問題
    // 各種開發(fā)服務器設置
    host: 'localhost', // 地址
    port: 8080, // 端口號設置,端口號占用出現(xiàn)問題可在此處修改
    //是否在編譯(輸入命令行npm run dev)后打開http://localhost:8080/頁面饱搏,以前配置為true帆啃,近些版本改為false
    autoOpenBrowser: false,  
    errorOverlay: true,  //瀏覽器錯誤提示
    notifyOnErrors: true,  //跨平臺錯誤提示
    poll: false, //使用文件系統(tǒng)(file system)獲取文件改動的通知devServer.watchOptions
    // 如果用了ESlint,代碼會捆綁和連接錯誤和警告顯示在控制臺中
    useEslint: true,
    //eslint錯誤和警告也將顯示在瀏覽器的錯誤中窍帝。
    showEslintErrorsInOverlay: false,
    devtool: 'cheap-module-eval-source-map',  //增加調試,該屬性為原始源代碼(僅限行)不可在生產(chǎn)環(huán)境中使用
    // 如果在devtools中調試vue文件有問題诽偷,請將其設置為false——這可能會有幫助坤学。
    cacheBusting: true,  //使緩存失效
    //代碼壓縮后進行調bug定位將非常困難疯坤,于是引入sourcemap記錄壓縮前后的位置信息記錄,
    //當產(chǎn)生錯誤時直接定位到未壓縮前的位置深浮,將大大的方便我們調試
    cssSourceMap: true  
  },
  // 生產(chǎn)環(huán)境下面的配置
  build: {
    //index編譯后生成的位置和名字压怠,根據(jù)需要改變后綴,比如index.php
    index: path.resolve(__dirname, '../dist/index.html'),
    assetsRoot: path.resolve(__dirname, '../dist'),  //編譯后存放生成環(huán)境代碼的位置
    assetsSubDirectory: 'static',  //js,css,images存放文件夾名
    //發(fā)布的根目錄飞苇,通常本地打包dist后打開文件會報錯菌瘫,此處修改為./。
    //如果是上線的文件布卡,可根據(jù)文件存放位置進行更改路徑
    assetsPublicPath: '/',
    productionSourceMap: true,
    devtool: '#source-map',
    //unit的gzip命令用來壓縮文件雨让,gzip模式下需要壓縮的文件的擴展名有js和css
    // 默認情況下,像Surge或Netlify這樣的流行靜態(tài)主機已經(jīng)為您壓縮了所有靜態(tài)資源忿等。
    //在設置為“true”之前栖忠,請確保安裝了:
    // npm install --save-dev compression-webpack-plugin
    productionGzip: false,
    productionGzipExtensions: ['js', 'css'],
    // 在構建完成后,用額外的參數(shù)運行構建命令來查看包分析器報告:
    // npm run build --report
    // 設置為“true”或“false”贸街,以打開或關閉它
    bundleAnalyzerReport: process.env.npm_config_report
  }
}

build文件夾

├─build 
│ ├─build.js 
│ ├─check-versions.js 
│ ├─utils.js 
│ ├─vue-loader.conf.js 
│ ├─webpack.base.conf.js 
│ ├─webpack.dev.conf.js 
│ ├─webpack.prod.conf.js 

build/build.js

該文件作用庵寞,即構建生產(chǎn)版本。package.json中的scriptsbuild就是node build/build.js薛匪,輸入命令行npm run build對該文件進行編譯生成生產(chǎn)環(huán)境的代碼捐川。

'use strict'
require('./check-versions')()  //調用檢查版本的文件,加()代表直接調用該函數(shù)
process.env.NODE_ENV = 'production'  //設置當前環(huán)境為production
//下面定義常量引入插件
const ora = require('ora')  //加載動畫
const rm = require('rimraf')  //Node環(huán)境下rm -rf的命令庫
const path = require('path')  //文件路徑處理
const chalk = require('chalk')  //對文案輸出的一個彩色設置
const webpack = require('webpack')
const config = require('../config')  //默認讀取下面的index.js文件
const webpackConfig = require('./webpack.prod.conf')  //生產(chǎn)環(huán)境下的webpack配置
const spinner = ora('building for production...')
spinner.start()  //調用start的方法實現(xiàn)加載動畫逸尖,優(yōu)化用戶體驗
//先刪除dist文件再生成新文件古沥,因為有時候會使用hash來命名,刪除整個文件可避免冗余
rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
  if (err) throw err
  webpack(webpackConfig, (err, stats) => {
    spinner.stop()  //停止loading
    if (err) throw err
    process.stdout.write(stats.toString({
      colors: true,
      modules: false,
      //如果使用的是ts-loader冷溶,將其設置為true將會在構建期間出現(xiàn)TypeScript錯誤
      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'
    ))
  })
})

build/check-version.js

該文件用于檢測nodenpm的版本渐白,實現(xiàn)版本依賴。

'use strict'
const chalk = require('chalk')
const semver = require('semver')  //對版本進行檢查
const packageConfig = require('../package.json')
const shell = require('shelljs')
function exec (cmd) {
  //返回通過child_process模塊的新建子進程逞频,執(zhí)行Unix系統(tǒng)命令后轉成沒有空格的字符串
  return require('child_process').execSync(cmd).toString().trim()
}
const versionRequirements = [
  {
    name: 'node',
    currentVersion: semver.clean(process.version),  //使用semver格式化版本
    versionRequirement: packageConfig.engines.node  //獲取package.json中設置的node版本
  }
]
if (shell.which('npm')) {
  versionRequirements.push({
    name: 'npm',
    // 自動調用npm --version命令纯衍,并且把參數(shù)返回給exec函數(shù),從而獲取純凈的版本號
    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]
    if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {
      //上面這個判斷就是如果版本號不符合package.json文件中指定的版本號苗胀,就執(zhí)行下面錯誤提示的代碼
      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/utils.js

utils.js是一個用來處理css的文件襟诸。

'use strict'
const path = require('path')
const config = require('../config')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const packageConfig = require('../package.json')
//導出文件的位置,根據(jù)環(huán)境判斷開發(fā)環(huán)境和生產(chǎn)環(huán)境基协,
//為config文件中index.js文件中定義的build.assetsSubDirectory或dev.assetsSubDirectory
exports.assetsPath = function (_path) {
  const assetsSubDirectory = process.env.NODE_ENV === 'production'
    ? config.build.assetsSubDirectory
    : config.dev.assetsSubDirectory
  //Node.js path 模塊提供了一些用于處理文件路徑的小工具
  return path.posix.join(assetsSubDirectory, _path)
}
//使用了css-loader和postcssLoader歌亲,通過options.usePostCSS屬性來判斷是否使用postcssLoader中壓縮等方法
exports.cssLoaders = function (options) {
  options = options || {}
  const cssLoader = {
    loader: 'css-loader',
    options: {  //css-loader的選項配置
      sourceMap: options.sourceMap  //根據(jù)參數(shù)是否要生成sourceMap文件
    }
  }
  const postcssLoader = {
    loader: 'postcss-loader',
    options: {
      sourceMap: options.sourceMap
    }
  }
  // 生成加載器字符串用于提取文本插件
  function generateLoaders (loader, loaderOptions) {  //生成loader
    const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader]
    if (loader) {
      loaders.push({
        loader: loader + '-loader', 
        //Object.assign是es6語法的淺復制,將loaderOptions和sourceMap合并成一個對象后復制完成賦值
        options: Object.assign({}, loaderOptions, {  
          sourceMap: options.sourceMap
        })
      })
    }
    // 在指定該選項時提取CSS
    if (options.extract) {  //如果options存在extract并且為true
      //ExtractTextPlugin可提取出文本澜驮,代表首先使用上面處理的loaders陷揪,當未能正確引入時使用vue-style-loader
      return ExtractTextPlugin.extract({  //ExtractTextPlugin分離js中引入的css文件
        use: loaders,  //處理loader
        fallback: 'vue-style-loader'  //沒有被提取分離時使用的loader
      })
    } else { 
      //返回vue-style-loader連接loaders的最終值
      return ['vue-style-loader'].concat(loaders)
    }
  }
  return {  //返回css類型對應的loader組成的對象 generateLoaders()來生成loader
    css: generateLoaders(),  //需要css-loader和vue-style-loader
    postcss: generateLoaders(),  //需要css-loader和postcssLoader 和vue-style-loader
    less: generateLoaders('less'),  //需要less-loader和vue-style-loader
    sass: generateLoaders('sass', { indentedSyntax: true }),  //需要sass-loader和vue-style-loader
    scss: generateLoaders('sass'),  //需要sass-loader和vue-style-loader
    stylus: generateLoaders('stylus'),  //需要stylus-loader和vue-style-loader
    styl: generateLoaders('stylus')  //需要stylus-loader和vue-style-loader
  }
}
// 為獨立樣式文件生成加載器(在.vue之外)
exports.styleLoaders = function (options) {
  const output = []
  //調用cssLoaders方法返回各類型的樣式對象
  const loaders = exports.cssLoaders(options)
  for (const extension in loaders) {  //遍歷loaders
    //根據(jù)遍歷獲得的key(extension)來得到value(loader)
    const loader = loaders[extension]
    output.push({
      test: new RegExp('\\.' + extension + '$'),  //處理文件的類型
      use: loader
    })
  }
  return output
}
exports.createNotifierCallback = () => { 
  //發(fā)送跨平臺通知系統(tǒng)
  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')
    })
  }
}

path.posix:提供對路徑方法的POSIX(可移植性操作系統(tǒng)接口)特定實現(xiàn)的訪問悍缠,即可跨平臺卦绣,區(qū)別于win32。
path.join:用于連接路徑飞蚓,會正確使用當前系統(tǒng)的路徑分隔符滤港,Unix系統(tǒng)是"/",Windows系統(tǒng)是""

vue-loader.conf.js

該文件的主要作用就是處理.vue文件趴拧,解析這個文件中的每個語言塊(template溅漾、scriptstyle),轉換成js可用的js模塊著榴。

'use strict'
const utils = require('./utils')
const config = require('../config')
const isProduction = process.env.NODE_ENV === 'production'
const sourceMapEnabled = isProduction
  ? config.build.productionSourceMap
  : config.dev.cssSourceMap
//處理項目中的css文件添履,生產(chǎn)環(huán)境和測試環(huán)境默認是打開sourceMap,
//而extract中的提取樣式到單獨文件只有在生產(chǎn)環(huán)境中才需要
module.exports = {
  loaders: utils.cssLoaders({
    sourceMap: sourceMapEnabled,
    extract: isProduction
  }),
  cssSourceMap: sourceMapEnabled,
  cacheBusting: config.dev.cacheBusting,
  // 在模版編譯過程中兄渺,編譯器可以將某些屬性缝龄,如 src 路徑,轉換為require調用挂谍,以便目標資源可以由 webpack 處理.
  transformToRequire: {
    video: ['src', 'poster'],
    source: 'src',
    img: 'src',
    image: 'xlink:href'
  }
}

webpack.base.conf.js

webpack.base.conf.js是開發(fā)和生產(chǎn)共同使用提出來的基礎配置文件叔壤,主要實現(xiàn)配制入口,配置輸出環(huán)境口叙,配置模塊resolve和插件等炼绘。

'use strict'
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)
}
const createLintingRule = () => ({
  test: /\.(js|vue)$/,
  loader: 'eslint-loader',
  enforce: 'pre',
  include: [resolve('src'), resolve('test')],
  options: {
    formatter: require('eslint-friendly-formatter'),
    emitWarning: !config.dev.showEslintErrorsInOverlay
  }
})
//path.join將路徑片段進行拼接,而path.resolve將以/開始的路徑片段作為根目錄妄田,在此之前的路徑將會被丟棄
//path.join('/a', '/b') // 'a/b',path.resolve('/a', '/b') // '/b'
module.exports = {
  context: path.resolve(__dirname, '../'),
  //配置入口俺亮,默認為單頁面所以只有app一個入口
  entry: {
    app: './src/main.js'
  },
  //配置出口,默認是/dist作為目標文件夾的路徑
  output: {
    path: config.build.assetsRoot,  //導出目錄的絕對路徑
    filename: '[name].js',  //導出文件的文件名
    //生產(chǎn)模式或開發(fā)模式下的html疟呐、js等文件內(nèi)部引用的公共路徑
    publicPath: process.env.NODE_ENV === 'production'
      ? config.build.assetsPublicPath
      : config.dev.assetsPublicPath
  },
  resolve: {  //文件解析
    //自動解析確定的擴展名脚曾,使導入模塊時不帶擴展名
    extensions: ['.js', '.vue', '.json'],
    //創(chuàng)建import或require的別名
    alias: {
      'vue$': 'vue/dist/vue.esm.js',
      '@': resolve('src'),
    }
  },
  module: {  //模塊解析,如何處理項目中不同類型的模塊
    //使用vue-loader將vue文件轉化成js的模塊
    rules: [
      ...(config.dev.useEslint ? [createLintingRule()] : []),
      {
        test: /\.vue$/,
        loader: 'vue-loader',
        options: vueLoaderConfig  //對vue-loader做額外的選項配置
      },
      //js文件需要通過babel-loader進行編譯成es5文件以及壓縮等操作
      {
        test: /\.js$/,
        loader: 'babel-loader',
        //必須處理包含src启具、test和client的文件夾
        include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')]
      },
      //圖片本讥、音像、字體都使用url-loader進行處理鲁冯,超過10000會編譯成base64
      {
        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.7位hash值.擴展名
          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]')
        }
      }
    ]
  },
  //以下選項是Node.js全局變量或模塊拷沸,這里主要是防止webpack注入一些Node.js的東西到vue中 
  node: {
    setImmediate: false,
    // 防止webpack向節(jié)點本地模塊注入模擬,這對客戶端來說是沒有意義的
    dgram: 'empty',
    fs: 'empty',
    net: 'empty',
    tls: 'empty',
    child_process: 'empty'
  }
}

webpack.dev.conf.js

'use strict'
const utils = require('./utils')
const webpack = require('webpack')
const config = require('../config')
//通過webpack-merge實現(xiàn)webpack.dev.conf.js對wepack.base.config.js的繼承
const merge = require('webpack-merge')
const path = require('path')
const baseWebpackConfig = require('./webpack.base.conf')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
//美化webpack的錯誤信息和日志的插件
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
// 查看空閑端口位置薯演,默認情況下搜索8000這個端口
const portfinder = require('portfinder')
/processs為node的一個全局對象獲取當前程序的環(huán)境變量撞芍,即host
const HOST = process.env.HOST
const PORT = process.env.PORT && Number(process.env.PORT)
const devWebpackConfig = merge(baseWebpackConfig, {
  module: {
    //規(guī)則是工具utils中處理出來的styleLoaders,生成了css跨扮,less,postcss等規(guī)則
    rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true })
  },
  // cheap-module-eval-source-map is faster for development
  devtool: config.dev.devtool,  //增強調試序无,上文有提及
  //此處的配置都是在config的index.js中設定好了
  devServer: {
    clientLogLevel: 'warning',  //控制臺顯示的選項有none, error, warning 或者 info
    //當使用H5 History API時验毡,任意的404響應都可能需要被替代為index.html
    historyApiFallback: {
      rewrites: [
        { from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') },
      ],
    },
    hot: true,  //熱加載
    contentBase: false, // since we use CopyWebpackPlugin.
    compress: true,  //壓縮
    host: HOST || config.dev.host,
    port: PORT || config.dev.port,
    open: config.dev.autoOpenBrowser,  //調試時自動打開瀏覽器
    overlay: config.dev.errorOverlay
      ? { warnings: false, errors: true }
      : false,  // warning 和 error 都要顯示
    publicPath: config.dev.assetsPublicPath,
    proxy: config.dev.proxyTable,  //接口代理
    quiet: true, //控制臺是否禁止打印警告和錯誤,若用FriendlyErrorsPlugin 此處為 true
    watchOptions: {
      poll: config.dev.poll,
    }
  },
  plugins: [
    new webpack.DefinePlugin({  //編譯時配置的全局變量
      'process.env': require('../config/dev.env')  //當前環(huán)境為開發(fā)環(huán)境
    }),  
    //模塊熱替換插件,修改模塊時不需要刷新頁面
    new webpack.HotModuleReplacementPlugin(),  //熱更新插件
    new webpack.NamedModulesPlugin(), // 顯示文件的正確名字
    //不觸發(fā)錯誤愉镰,即編譯后運行的包正常運行
    new webpack.NoEmitOnErrorsPlugin(),
    //自動生成html文件米罚,比如編譯后文件的引用
    new HtmlWebpackPlugin({
      filename: 'index.html',  //生成的文件名
      template: 'index.html',  //模板
      inject: true
    }),
    //復制插件
    new CopyWebpackPlugin([
      {
        from: path.resolve(__dirname, '../static'),
        to: config.dev.assetsSubDirectory,
        ignore: ['.*']  //忽略.*的文件
      }
    ])
  ]
})
module.exports = new Promise((resolve, reject) => {
  portfinder.basePort = process.env.PORT || config.dev.port
  //查找端口號
  portfinder.getPort((err, port) => {
    if (err) {
      reject(err)
    } else {
      // 發(fā)布e2e測試所需的新端口
      //端口被占用時就重新設置evn和devServer的端口
      process.env.PORT = port
      // 添加端口到devServer配置
      devWebpackConfig.devServer.port = port
      // 添加 FriendlyErrorsPlugin
      devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({
        compilationSuccessInfo: {
          messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`],
        },
        onErrors: config.dev.notifyOnErrors
        ? utils.createNotifierCallback()
        : undefined
      }))
      resolve(devWebpackConfig)
    }
  })
})

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')
const baseWebpackConfig = require('./webpack.base.conf')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
const env = require('../config/prod.env')
const webpackConfig = merge(baseWebpackConfig, {
  module: {
    //調用utils.styleLoaders的方法
    rules: utils.styleLoaders({
      sourceMap: config.build.productionSourceMap,  //開啟調試的模式。默認為true
      extract: true,
      usePostCSS: true
    })
  },
  devtool: config.build.productionSourceMap ? config.build.devtool : 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  //配置全局環(huán)境為生產(chǎn)環(huán)境
    }),
    new UglifyJsPlugin({  //js壓縮插件
      uglifyOptions: {
        compress: {  //壓縮配置
          warnings: false  //警告:true保留警告拔莱,false不保留
        }
      },
      sourceMap: config.build.productionSourceMap,  //生成sourceMap文件
      parallel: true
    }),
    //抽取文本碗降。比如打包之后的index頁面有css插入,就是這個插件抽取出來的塘秦,減少請求
    new ExtractTextPlugin({
      filename: utils.assetsPath('css/[name].[contenthash].css'),  //分離出來的css文件名
      allChunks: true,
    }),
    //壓縮提取出的css讼渊,并解決ExtractTextPlugin分離出的js重復問題(多個文件引用同一個css文件)
    new OptimizeCSSPlugin({
      cssProcessorOptions: config.build.productionSourceMap
        ? { safe: true, map: { inline: false } }
        : { safe: true }
    }),
    new HtmlWebpackPlugin({  //生成html的插件,引入css文件和js文件
      filename: config.build.index,  //生成的html的文件名
      template: 'index.html',  //依據(jù)的模板
      inject: true,  //注入的js文件會被放在body標簽中尊剔,當值為head的時候將被放在head標簽中
      minify: {  //壓縮配置
        removeComments: true,  //刪除html中的注釋代碼
        collapseWhitespace: true,  //刪除html中的空格
        removeAttributeQuotes: true  //刪除html元素中屬性的引號
      },
      chunksSortMode: 'dependency'  //模塊排序爪幻,按照我們需要的順序排序
    }),
    new webpack.HashedModuleIdsPlugin(),
    new webpack.optimize.ModuleConcatenationPlugin(),
    new webpack.optimize.CommonsChunkPlugin({  //分離公共js到vendor中
      name: 'vendor',
      minChunks (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',
      minChunks: Infinity
    }),
    new webpack.optimize.CommonsChunkPlugin({
      name: 'app',
      async: 'vendor-async',
      children: true,
      minChunks: 3
    }),
    //復制靜態(tài)資源,將static文件內(nèi)的內(nèi)容復制到指定文件夾
    new CopyWebpackPlugin([
      {
        from: path.resolve(__dirname, '../static'),
        to: config.build.assetsSubDirectory,
        ignore: ['.*']  //忽略.*文件
      }
    ])
  ]
})
if (config.build.productionGzip) {  //配置文件開啟gzip壓縮
  //引入壓縮文件的組件须误,該插件會對生成的文件進行壓縮挨稿,生成一個.gz文件
  const CompressionWebpackPlugin = require('compression-webpack-plugin')
  webpackConfig.plugins.push(
    new CompressionWebpackPlugin({
      asset: '[path].gz[query]',  //目標文件名
      algorithm: 'gzip',  //使用gzip壓縮
      test: new RegExp(  //滿足正值表達式的文件會被壓縮
        '\\.(' +
        config.build.productionGzipExtensions.join('|') +
        ')$'
      ),
      threshold: 10240,  //資源文件大于10kb的時候會被壓縮
      minRatio: 0.8  //最小壓縮比達到0.8的時候會被壓縮
    })
  )
}
if (config.build.bundleAnalyzerReport) {
  const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
  webpackConfig.plugins.push(new BundleAnalyzerPlugin())
}
module.exports = webpackConfig
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市京痢,隨后出現(xiàn)的幾起案子奶甘,更是在濱河造成了極大的恐慌,老刑警劉巖祭椰,帶你破解...
    沈念sama閱讀 216,651評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件臭家,死亡現(xiàn)場離奇詭異,居然都是意外死亡方淤,警方通過查閱死者的電腦和手機钉赁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來携茂,“玉大人你踩,你說我怎么就攤上這事∫亟” “怎么了姓蜂?”我有些...
    開封第一講書人閱讀 162,931評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長医吊。 經(jīng)常有香客問我钱慢,道長,這世上最難降的妖魔是什么卿堂? 我笑而不...
    開封第一講書人閱讀 58,218評論 1 292
  • 正文 為了忘掉前任束莫,我火速辦了婚禮懒棉,結果婚禮上,老公的妹妹穿的比我還像新娘览绿。我一直安慰自己策严,他們只是感情好,可當我...
    茶點故事閱讀 67,234評論 6 388
  • 文/花漫 我一把揭開白布饿敲。 她就那樣靜靜地躺著妻导,像睡著了一般。 火紅的嫁衣襯著肌膚如雪怀各。 梳的紋絲不亂的頭發(fā)上倔韭,一...
    開封第一講書人閱讀 51,198評論 1 299
  • 那天,我揣著相機與錄音瓢对,去河邊找鬼寿酌。 笑死,一個胖子當著我的面吹牛硕蛹,可吹牛的內(nèi)容都是我干的醇疼。 我是一名探鬼主播,決...
    沈念sama閱讀 40,084評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼法焰,長吁一口氣:“原來是場噩夢啊……” “哼秧荆!你這毒婦竟也來了?” 一聲冷哼從身側響起壶栋,我...
    開封第一講書人閱讀 38,926評論 0 274
  • 序言:老撾萬榮一對情侶失蹤辰如,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后贵试,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體琉兜,經(jīng)...
    沈念sama閱讀 45,341評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,563評論 2 333
  • 正文 我和宋清朗相戀三年毙玻,在試婚紗的時候發(fā)現(xiàn)自己被綠了豌蟋。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,731評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡桑滩,死狀恐怖梧疲,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情运准,我是刑警寧澤幌氮,帶...
    沈念sama閱讀 35,430評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站胁澳,受9級特大地震影響该互,放射性物質發(fā)生泄漏。R本人自食惡果不足惜韭畸,卻給世界環(huán)境...
    茶點故事閱讀 41,036評論 3 326
  • 文/蒙蒙 一宇智、第九天 我趴在偏房一處隱蔽的房頂上張望蔓搞。 院中可真熱鬧,春花似錦随橘、人聲如沸喂分。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,676評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蒲祈。三九已至,卻和暖如春蜒车,著一層夾襖步出監(jiān)牢的瞬間讳嘱,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,829評論 1 269
  • 我被黑心中介騙來泰國打工酿愧, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人邀泉。 一個月前我還...
    沈念sama閱讀 47,743評論 2 368
  • 正文 我出身青樓嬉挡,卻偏偏與公主長得像,于是被迫代替她去往敵國和親汇恤。 傳聞我的和親對象是個殘疾皇子庞钢,可洞房花燭夜當晚...
    茶點故事閱讀 44,629評論 2 354

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