[toc]
目錄
結構預覽
├─build // 儲存一些webpack的初始化配置,專案構建
│ ├─build.js // 生產環境構建
│ ├─check-version.js // 檢查npm、node版本
│ ├─vue-loader.conf.js // webpack loader配置
│ ├─webpack.base.conf.js// webpack基礎配置
│ ├─webpack.dev.conf.js // 開發環境配置,構建本地開發伺服器
│ ├─webpack.prod.conf.js// 生產環境的配置
│
├─config // config資料夾儲存一些專案初始化的配置
│ ├─dev.env.js // 開發環境的配置
│ ├─index.js // 專案一些配置變數
│ ├─prod.env.js // 生產環境的配置
│
├─dist // 打包後的專案
├─node_modules // 依賴包
│
├─src // 原始碼目錄
│ ├─assets // 靜態檔案目錄
│ ├─components // 元件檔案
│ ├─router // 路由
│ ├─App.vue // 是專案入口檔案
│ ├─main.js // 是專案的核心檔案,入口
├─static // 靜態資源目錄
├─.babelrc // Babel的配置檔案
├─.editorconfig // 程式碼規範配置檔案
├─.gitignore // git忽略配置檔案
├─.postcssrc.js // postcss外掛配置檔案
├─index.html // 頁面入口檔案
├─package-lock.json // 專案包管控檔案
├─package.json // 專案配置
└─README.md // 專案說明書
結構解析
build
dev-server.js
首先來看執行”npm run dev”時候最先執行的build/dev-server.js檔案。該檔案主要完成下面幾件事情:
- 檢查node和npm的版本、引入相關外掛和配置
- webpack對原始碼進行編譯打包並返回compiler物件
- 建立express伺服器
- 配置開發中介軟體(webpack-dev-middleware)和+ 熱過載中介軟體(webpack-hot-middleware)
- 掛載代理服務和中介軟體
- 配置靜態資源
- 啟動伺服器監聽特定埠(8080)
- 自動開啟瀏覽器並開啟特定網址(localhost:8080)
說明: express伺服器提供靜態檔案服務,不過它還使用了http-proxy-middleware,一個http請求代理的中介軟體。前端開發過程中需要使用到後臺的API的話,可以通過配置proxyTable來將相應的後臺請求代理到專用的API伺服器。
// 檢查NodeJS和npm的版本
require(`./check-versions`)()
// 獲取基本配置
var config = require(`../config`)
// 如果Node的環境變數中沒有設定當前的環境(NODE_ENV),則使用config中的dev環境配置作為當前的環境
if (!process.env.NODE_ENV) {
process.env.NODE_ENV = JSON.parse(config.dev.env.NODE_ENV)
}
// opn是一個可以呼叫預設軟體開啟網址、圖片、檔案等內容的外掛
// 這裡用它來呼叫預設瀏覽器開啟dev-server監聽的埠,例如:localhost:8080
var opn = require(`opn`)
var path = require(`path`)
var express = require(`express`)
var webpack = require(`webpack`)
// http-proxy-middleware是一個express中介軟體,用於將http請求代理到其他伺服器
// 例:localhost:8080/api/xxx --> localhost:3000/api/xxx
// 這裡使用該外掛可以將前端開發中涉及到的請求代理到提供服務的後臺伺服器上,方便與伺服器對接
var proxyMiddleware = require(`http-proxy-middleware`)
// 開發環境下的webpack配置
var webpackConfig = require(`./webpack.dev.conf`)
// dev-server 監聽的埠,如果沒有在命令列傳入埠號,則使用config.dev.port設定的埠,例如8080
var port = process.env.PORT || config.dev.port
// 用於判斷是否要自動開啟瀏覽器的布林變數,當配置檔案中沒有設定自動開啟瀏覽器的時候其值為 false
var autoOpenBrowser = !!config.dev.autoOpenBrowser
// HTTP代理表,指定規則,將某些API請求代理到相應的伺服器
var proxyTable = config.dev.proxyTable
// 建立express伺服器
var app = express()
// webpack根據配置開始編譯打包原始碼並返回compiler物件
var compiler = webpack(webpackConfig)
// webpack-dev-middleware將webpack編譯打包後得到的產品檔案存放在記憶體中而沒有寫進磁碟
// 將這個中介軟體掛到express上使用之後即可提供這些編譯後的產品檔案服務
var devMiddleware = require(`webpack-dev-middleware`)(compiler, {
publicPath: webpackConfig.output.publicPath, // 設定訪問路徑為webpack配置中的output裡面所對應的路徑
quiet: true // 設定為true,使其不要在控制檯輸出日誌
})
// webpack-hot-middleware,用於實現熱過載功能的中介軟體
var hotMiddleware = require(`webpack-hot-middleware`)(compiler, {
log: false, // 關閉控制檯的日誌輸出
heartbeat: 2000 // 傳送心跳包的頻率
})
// webpack(重新)編譯打包完成後並將js、css等檔案inject到html檔案之後,通過熱過載中介軟體強制頁面重新整理
compiler.plugin(`compilation`, function (compilation) {
compilation.plugin(`html-webpack-plugin-after-emit`, function (data, cb) {
hotMiddleware.publish({ action: `reload` })
cb()
})
})
// 根據 proxyTable 中的代理請求配置來設定express伺服器的http代理規則
Object.keys(proxyTable).forEach(function (context) {
var options = proxyTable[context]
// 格式化options,例如將`www.example.com`變成{ target: `www.example.com` }
if (typeof options === `string`) {
options = { target: options }
}
app.use(proxyMiddleware(options.filter || context, options))
})
// handle fallback for HTML5 history API
// 重定向不存在的URL,用於支援SPA(單頁應用)
// 例如使用vue-router並開啟了history模式
app.use(require(`connect-history-api-fallback`)())
// serve webpack bundle output
// 掛載webpack-dev-middleware中介軟體,提供webpack編譯打包後的產品檔案服務
app.use(devMiddleware)
// enable hot-reload and state-preserving
// compilation error display
// 掛載熱過載中介軟體
app.use(hotMiddleware)
// serve pure static assets
// 提供static資料夾上的靜態檔案服務
var staticPath = path.posix.join(config.dev.assetsPublicPath, config.dev.assetsSubDirectory)
app.use(staticPath, express.static(`./static`))
// 訪問連結
var uri = `http://localhost:` + port
// 建立promise,在應用服務啟動之後resolve
// 便於外部檔案require了這個dev-server之後的程式碼編寫
var _resolve
var readyPromise = new Promise(resolve => {
_resolve = resolve
})
console.log(`> Starting dev server...`)
// webpack-dev-middleware等待webpack完成所有編譯打包之後輸出提示語到控制檯,表明服務正式啟動
// 服務正式啟動才自動開啟瀏覽器進入頁面
devMiddleware.waitUntilValid(() => {
console.log(`> Listening at ` + uri + `
`)
// when env is testing, don`t need open it
if (autoOpenBrowser && process.env.NODE_ENV !== `testing`) {
opn(uri)
}
_resolve()
})
// 啟動express伺服器並監聽相應的埠
var server = app.listen(port)
// 暴露本模組的功能給外部使用,例如下面這種用法
// var devServer = require(`./build/dev-server`)
// devServer.ready.then(() => {...})
// if (...) { devServer.close() }
module.exports = {
ready: readyPromise,
close: () => {
server.close()
}
}
webpack.base.conf.js
從程式碼中看到,dev-server使用的webpack配置來自build/webpack.dev.conf.js檔案(測試環境下使用的是build/webpack.prod.conf.js,這裡暫時不考慮測試環境)。而build/webpack.dev.conf.js中又引用了webpack.base.conf.js,所以這裡我先分析webpack.base.conf.js。
webpack.base.conf.js主要完成了下面這些事情:
- 配置webpack編譯入口
- 配置webpack輸出路徑和命名規則
- 配置模組resolve規則
- 配置不同型別模組的處理規則
說明: 這個配置裡面只配置了.js、.vue、圖片、字型等幾類檔案的處理規則,如果需要處理其他檔案可以在module.rules裡面另行配置。
var path = require(`path`)
var fs = require(`fs`)
var utils = require(`./utils`)
var config = require(`../config`)
var vueLoaderConfig = require(`./vue-loader.conf`)
// 獲取絕對路徑
function resolve (dir) {
return path.join(__dirname, `..`, dir)
}
module.exports = {
// webpack入口檔案
entry: {
app: `./src/main.js`
},
// webpack輸出路徑和命名規則
output: {
// webpack輸出的目標資料夾路徑(例如:/dist)
path: config.build.assetsRoot,
// webpack輸出bundle檔案命名格式
filename: `[name].js`,
// webpack編譯輸出的釋出路徑(例如`//cdn.xxx.com/app/`)
publicPath: process.env.NODE_ENV === `production`
? config.build.assetsPublicPath
: config.dev.assetsPublicPath
},
// 模組resolve的規則
resolve: {
extensions: [`.js`, `.vue`, `.json`],
// 別名,方便引用模組,例如有了別名之後,
// import Vue from `vue/dist/vue.common.js`可以寫成 import Vue from `vue`
alias: {
`vue$`: `vue/dist/vue.esm.js`,
`@`: resolve(`src`),
},
symlinks: false
},
// 不同型別模組的處理規則
module: {
rules: [
{// 對src和test資料夾下的.js和.vue檔案使用eslint-loader進行程式碼規範檢查
test: /.(js|vue)$/,
loader: `eslint-loader`,
enforce: `pre`,
include: [resolve(`src`), resolve(`test`)],
options: {
formatter: require(`eslint-friendly-formatter`)
}
},
{// 對所有.vue檔案使用vue-loader進行編譯
test: /.vue$/,
loader: `vue-loader`,
options: vueLoaderConfig
},
{// 對src和test資料夾下的.js檔案使用babel-loader將es6+的程式碼轉成es5
test: /.js$/,
loader: `babel-loader`,
include: [resolve(`src`), resolve(`test`)]
},
{// 對圖片資原始檔使用url-loader
test: /.(png|jpe?g|gif|svg)(?.*)?$/,
loader: `url-loader`,
options: {
// 小於10K的圖片轉成base64編碼的dataURL字串寫到程式碼中
limit: 10000,
// 其他的圖片轉移到靜態資原始檔夾
name: utils.assetsPath(`img/[name].[hash:7].[ext]`)
}
},
{// 對多媒體資原始檔使用url-loader
test: /.(mp4|webm|ogg|mp3|wav|flac|aac)(?.*)?$/,
loader: `url-loader`,
options: {
// 小於10K的資源轉成base64編碼的dataURL字串寫到程式碼中
limit: 10000,
// 其他的資源轉移到靜態資原始檔夾
name: utils.assetsPath(`media/[name].[hash:7].[ext]`)
}
},
{// 對字型資原始檔使用url-loader
test: /.(woff2?|eot|ttf|otf)(?.*)?$/,
loader: `url-loader`,
options: {
// 小於10K的資源轉成base64編碼的dataURL字串寫到程式碼中
limit: 10000,
// 其他的資源轉移到靜態資原始檔夾
name: utils.assetsPath(`fonts/[name].[hash:7].[ext]`)
}
}
]
}
}
webpack.dev.conf.js
接下來看webpack.dev.conf.js,這裡面在webpack.base.conf的基礎上增加完善了開發環境下面的配置,主要包括下面幾件事情:
- 將webpack的熱過載客戶端程式碼新增到每個entry對應的應用
- 合併基礎的webpack配置
- 配置樣式檔案的處理規則,styleLoaders
- 配置Source Maps
- 配置webpack外掛
var utils = require(`./utils`)
var webpack = require(`webpack`)
var config = require(`../config`)
// webpack-merge是一個可以合併陣列和物件的外掛
var merge = require(`webpack-merge`)
var baseWebpackConfig = require(`./webpack.base.conf`)
// html-webpack-plugin用於將webpack編譯打包後的產品檔案注入到html模板中
// 即自動在index.html裡面加上<link>和<script>標籤引用webpack打包後的檔案
var HtmlWebpackPlugin = require(`html-webpack-plugin`)
// friendly-errors-webpack-plugin用於更友好地輸出webpack的警告、錯誤等資訊
var FriendlyErrorsPlugin = require(`friendly-errors-webpack-plugin`)
// add hot-reload related code to entry chunks
// 給每個入口頁面(應用)加上dev-client,用於跟dev-server的熱過載外掛通訊,實現熱更新
Object.keys(baseWebpackConfig.entry).forEach(function (name) {
baseWebpackConfig.entry[name] = [`./build/dev-client`].concat(baseWebpackConfig.entry[name])
})
module.exports = merge(baseWebpackConfig, {
module: {
// 樣式檔案的處理規則,對css/sass/scss等不同內容使用相應的styleLoaders
// 由utils配置出各種型別的預處理語言所需要使用的loader,例如sass需要使用sass-loader
rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap })
},
// cheap-module-eval-source-map is faster for development
// 使用這種source-map更快
devtool: `#cheap-module-eval-source-map`,
// webpack外掛
plugins: [
new webpack.DefinePlugin({
`process.env`: config.dev.env
}),
// 開啟webpack熱更新功能
new webpack.HotModuleReplacementPlugin(),
// webpack編譯過程中出錯的時候跳過報錯階段,不會阻塞編譯,在編譯結束後報錯
new webpack.NoEmitOnErrorsPlugin(),
// 自動將依賴注入html模板,並輸出最終的html檔案到目標資料夾
new HtmlWebpackPlugin({
filename: `index.html`,
template: `index.html`,
inject: true
}),
new FriendlyErrorsPlugin()
]
})
utils
此配置檔案是vue開發環境的wepack相關配置檔案,主要用來處理css-loader和vue-style-loader
// 引入nodejs路徑模組
var path = require(`path`)
// 引入config目錄下的index.js配置檔案
var config = require(`../config`)
// 引入extract-text-webpack-plugin外掛,用來將css提取到單獨的css檔案中
// 詳情請看(1)
var ExtractTextPlugin = require(`extract-text-webpack-plugin`)
// exports其實就是一個物件,用來匯出方法的最終還是使用module.exports,此處匯出assetsPath
exports.assetsPath = function (_path) {
// 如果是生產環境assetsSubDirectory就是`static`,否則還是`static`,哈哈哈
var assetsSubDirectory = process.env.NODE_ENV === `production`
? config.build.assetsSubDirectory
: config.dev.assetsSubDirectory
// path.join和path.posix.join的區別就是,前者返回的是完整的路徑,後者返回的是完整路徑的相對根路徑
// 也就是說path.join的路徑是C:a/a/b/xiangmu/b,那麼path.posix.join就是b
return path.posix.join(assetsSubDirectory, _path)
// 所以這個方法的作用就是返回一個乾淨的相對根路徑
}
// 下面是匯出cssLoaders的相關配置
exports.cssLoaders = function (options) {
// options如果沒值就是空物件
options = options || {}
// cssLoader的基本配置
var cssLoader = {
loader: `css-loader`,
options: {
// options是用來傳遞引數給loader的
// minimize表示壓縮,如果是生產環境就壓縮css程式碼
minimize: process.env.NODE_ENV === `production`,
// 是否開啟cssmap,預設是false
sourceMap: options.sourceMap
}
}
// generate loader string to be used with extract text plugin
function generateLoaders (loader, loaderOptions) {
// 將上面的基礎cssLoader配置放在一個陣列裡面
var loaders = [cssLoader]
// 如果該函式傳遞了單獨的loader就加到這個loaders陣列裡面,這個loader可能是less,sass之類的
if (loader) {
loaders.push({
// 載入對應的loader
loader: loader + `-loader`,
// Object.assign是es6的方法,主要用來合併物件的,淺拷貝
options: Object.assign({}, loaderOptions, {
sourceMap: options.sourceMap
})
})
}
// Extract CSS when that option is specified
// (which is the case during production build)
// 注意這個extract是自定義的屬性,可以定義在options裡面,主要作用就是當配置為true就把檔案單獨提取,false表示不單獨提取,這個可以在使用的時候單獨配置,瞬間覺得vue作者好牛逼
if (options.extract) {
return ExtractTextPlugin.extract({
use: loaders,
fallback: `vue-style-loader`
})
} else {
return [`vue-style-loader`].concat(loaders)
}
// 上面這段程式碼就是用來返回最終讀取和匯入loader,來處理對應型別的檔案
}
// https://vue-loader.vuejs.org/en/configurations/extract-css.html
return {
css: generateLoaders(), // css對應 vue-style-loader 和 css-loader
postcss: generateLoaders(), // postcss對應 vue-style-loader 和 css-loader
less: generateLoaders(`less`), // less對應 vue-style-loader 和 less-loader
sass: generateLoaders(`sass`, { indentedSyntax: true }), // sass對應 vue-style-loader 和 sass-loader
scss: generateLoaders(`sass`), // scss對應 vue-style-loader 和 sass-loader
stylus: generateLoaders(`stylus`), // stylus對應 vue-style-loader 和 stylus-loader
styl: generateLoaders(`stylus`) // styl對應 vue-style-loader 和 styl-loader
}
}
// Generate loaders for standalone style files (outside of .vue)
// 下面這個主要處理import這種方式匯入的檔案型別的打包,上面的exports.cssLoaders是為這一步服務的
exports.styleLoaders = function (options) {
var output = []
// 下面就是生成的各種css檔案的loader物件
var loaders = exports.cssLoaders(options)
for (var extension in loaders) {
// 把每一種檔案的laoder都提取出來
var loader = loaders[extension]
output.push({
// 把最終的結果都push到output陣列中,大事搞定
test: new RegExp(`\.` + extension + `$`),
use: loader
})
}
return output
}
extract-text-webpack-plugin外掛是用來將文字從bundle中提取到一個單獨的檔案中
const ExtractTextPlugin = require("extract-text-webpack-plugin");
module.exports = {
module: {
rules: [
{
test: /.css$/, //主要用來處理css檔案
use: ExtractTextPlugin.extract({
fallback: "style-loader", // fallback表示如果css檔案沒有成功匯入就使用style-loader匯入
use: "css-loader" // 表示使用css-loader從js讀取css檔案
})
}
],
plugins: [
new ExtractTextPlugin("styles.css") //表示生成styles.css檔案
]
}
}
vue-loader.conf.js
var utils = require(`./utils`)
var config = require(`../config`)
var isProduction = process.env.NODE_ENV === `production`
module.exports = {
// 處理.vue檔案中的樣式
loaders: utils.cssLoaders({
// 是否開啟source-map
sourceMap: isProduction
? config.build.productionSourceMap
: config.dev.cssSourceMap,
// 是否提取樣式到單獨的檔案
extract: isProduction
}),
transformToRequire: {
video: `src`,
source: `src`,
img: `src`,
image: `xlink:href`
}
}
dev-client.js
dev-client.js裡面主要寫了瀏覽器端程式碼,用於實現webpack的熱更新。
/* eslint-disable */
// 實現瀏覽器端的EventSource,用於跟伺服器雙向通訊
// webpack熱過載客戶端跟dev-server上的熱過載外掛之間需要進行雙向通訊
// 服務端webpack重新編譯後,會向客戶端推送資訊,告訴客戶端進行更新
require(`eventsource-polyfill`)
// webpack熱過載客戶端
var hotClient = require(`webpack-hot-middleware/client?noInfo=true&reload=true`)
// 客戶端收到更新動作,執行頁面重新整理
hotClient.subscribe(function (event) {
if (event.action === `reload`) {
window.location.reload()
}
})
build.js
執行”npm run build”的時候首先執行的是build/build.js檔案,build.js主要完成下面幾件事:
- loading動畫
- 刪除目標資料夾
- 執行webpack構建
- 輸出資訊
說明: webpack編譯之後會輸出到配置裡面指定的目標資料夾;刪除目標資料夾之後再建立是為了去除舊的內容,以免產生不可預測的影響。
// 檢查NodeJS和npm的版本
require(`./check-versions`)()
process.env.NODE_ENV = `production`
// ora,一個可以在終端顯示spinner的外掛
var ora = require(`ora`)
// rm,用於刪除檔案或資料夾的外掛
var rm = require(`rimraf`)
var path = require(`path`)
// chalk,用於在控制檯輸出帶顏色字型的外掛
var chalk = require(`chalk`)
var webpack = require(`webpack`)
var config = require(`../config`)
var webpackConfig = require(`./webpack.prod.conf`)
var spinner = ora(`building for production...`)
spinner.start() // 開啟loading動畫
// 首先將整個dist資料夾以及裡面的內容刪除,以免遺留舊的沒用的檔案
// 刪除完成後才開始webpack構建打包
rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
if (err) throw err
// 執行webpack構建打包,完成之後在終端輸出構建完成的相關資訊或者輸出報錯資訊並退出程式
webpack(webpackConfig, function (err, stats) {
spinner.stop()
if (err) throw err
process.stdout.write(stats.toString({
colors: true,
modules: false,
children: false,
chunks: false,
chunkModules: false
}) + `
`)
if (stats.hasErrors()) {
console.log(chalk.red(` Build failed with errors.
`))
process.exit(1)
}
console.log(chalk.cyan(` Build complete.
`))
console.log(chalk.yellow(
` Tip: built files are meant to be served over an HTTP server.
` +
` Opening index.html over file:// won`t work.
`
))
})
})
webpack.prod.conf.js
構建的時候用到的webpack配置來自webpack.prod.conf.js,該配置同樣是在webpack.base.conf基礎上的進一步完善。主要完成下面幾件事情:
- 合併基礎的webpack配置
- 配置樣式檔案的處理規則,styleLoaders
- 配置webpack的輸出
- 配置webpack外掛
- gzip模式下的webpack外掛配置
- webpack-bundle分析
說明: webpack外掛裡面多了醜化壓縮程式碼以及抽離css檔案等外掛。
var path = require(`path`)
var utils = require(`./utils`)
var webpack = require(`webpack`)
var config = require(`../config`)
var merge = require(`webpack-merge`)
var baseWebpackConfig = require(`./webpack.base.conf`)
// copy-webpack-plugin,用於將static中的靜態檔案複製到產品資料夾dist
var CopyWebpackPlugin = require(`copy-webpack-plugin`)
var HtmlWebpackPlugin = require(`html-webpack-plugin`)
var ExtractTextPlugin = require(`extract-text-webpack-plugin`)
// optimize-css-assets-webpack-plugin,用於優化和最小化css資源
var OptimizeCSSPlugin = require(`optimize-css-assets-webpack-plugin`)
var env = config.build.env
var webpackConfig = merge(baseWebpackConfig, {
module: {
// 樣式檔案的處理規則,對css/sass/scss等不同內容使用相應的styleLoaders
// 由utils配置出各種型別的預處理語言所需要使用的loader,例如sass需要使用sass-loader
rules: utils.styleLoaders({
sourceMap: config.build.productionSourceMap,
extract: true
})
},
// 是否使用source-map
devtool: config.build.productionSourceMap ? `#source-map` : false,
// webpack輸出路徑和命名規則
output: {
path: config.build.assetsRoot,
filename: utils.assetsPath(`js/[name].[chunkhash].js`),
chunkFilename: utils.assetsPath(`js/[id].[chunkhash].js`)
},
// webpack外掛
plugins: [
// http://vuejs.github.io/vue-loader/en/workflow/production.html
new webpack.DefinePlugin({
`process.env`: env
}),
// 醜化壓縮JS程式碼
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
},
sourceMap: true
}),
// extract css into its own file
// 將css提取到單獨的檔案
new ExtractTextPlugin({
filename: utils.assetsPath(`css/[name].[contenthash].css`)
}),
// Compress extracted CSS. We are using this plugin so that possible
// duplicated CSS from different components can be deduped.
// 優化、最小化css程式碼,如果只簡單使用extract-text-plugin可能會造成css重複
// 具體原因可以看npm上面optimize-css-assets-webpack-plugin的介紹
new OptimizeCSSPlugin({
cssProcessorOptions: {
safe: true
}
}),
// generate dist index.html with correct asset hash for caching.
// you can customize output by editing /index.html
// see https://github.com/ampedandwired/html-webpack-plugin
// 將產品檔案的引用注入到index.html
new HtmlWebpackPlugin({
filename: config.build.index,
template: `index.html`,
inject: true,
minify: {
// 刪除index.html中的註釋
removeComments: true,
// 刪除index.html中的空格
collapseWhitespace: true,
// 刪除各種html標籤屬性值的雙引號
removeAttributeQuotes: true
// more options:
// https://github.com/kangax/html-minifier#options-quick-reference
},
// necessary to consistently work with multiple chunks via CommonsChunkPlugin
// 注入依賴的時候按照依賴先後順序進行注入,比如,需要先注入vendor.js,再注入app.js
chunksSortMode: `dependency`
}),
// keep module.id stable when vender modules does not change
new webpack.HashedModuleIdsPlugin(),
// split vendor js into its own file
// 將所有從node_modules中引入的js提取到vendor.js,即抽取庫檔案
new webpack.optimize.CommonsChunkPlugin({
name: `vendor`,
minChunks: function (module, count) {
// any required modules inside node_modules are extracted to vendor
return (
module.resource &&
/.js$/.test(module.resource) &&
module.resource.indexOf(
path.join(__dirname, `../node_modules`)
) === 0
)
}
}),
// extract webpack runtime and module manifest to its own file in order to
// prevent vendor hash from being updated whenever app bundle is updated
// 從vendor中提取出manifest,原因如上
new webpack.optimize.CommonsChunkPlugin({
name: `manifest`,
chunks: [`vendor`]
}),
// copy custom static assets
// 將static資料夾裡面的靜態資源複製到dist/static
new CopyWebpackPlugin([
{
from: path.resolve(__dirname, `../static`),
to: config.build.assetsSubDirectory,
ignore: [`.*`]
}
])
]
})
// 如果開啟了產品gzip壓縮,則利用外掛將構建後的產品檔案進行壓縮
if (config.build.productionGzip) {
// 一個用於壓縮的webpack外掛
var CompressionWebpackPlugin = require(`compression-webpack-plugin`)
webpackConfig.plugins.push(
new CompressionWebpackPlugin({
asset: `[path].gz[query]`,
// 壓縮演算法
algorithm: `gzip`,
test: new RegExp(
`\.(` +
config.build.productionGzipExtensions.join(`|`) +
`)$`
),
threshold: 10240,
minRatio: 0.8
})
)
}
// 如果啟動了report,則通過外掛給出webpack構建打包後的產品檔案分析報告
if (config.build.bundleAnalyzerReport) {
var BundleAnalyzerPlugin = require(`webpack-bundle-analyzer`).BundleAnalyzerPlugin
webpackConfig.plugins.push(new BundleAnalyzerPlugin())
}
module.exports = webpackConfig
check-versions.js
// chalk, 用於在控制檯輸出帶顏色字型的外掛
var chalk = require(`chalk`)
// semver, 語義化版本檢查外掛(The semantic version parser used by npm)
var semver = require(`semver`)
var packageConfig = require(`../package.json`)
// shelljs, 執行Unix命令列的外掛
var shell = require(`shelljs`)
// 開闢子程式執行指令cmd並返回結果
function exec (cmd) {
return require(`child_process`).execSync(cmd).toString().trim()
}
// node和npm版本需求
var versionRequirements = [
{
name: `node`,
currentVersion: semver.clean(process.version),
versionRequirement: packageConfig.engines.node
}
]
if (shell.which(`npm`)) {
versionRequirements.push({
name: `npm`,
currentVersion: exec(`npm --version`),
versionRequirement: packageConfig.engines.npm
})
}
module.exports = function () {
var warnings = []
// 依次判斷版本是否符合要求
for (var i = 0; i < versionRequirements.length; i++) {
var mod = versionRequirements[i]
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 (var i = 0; i < warnings.length; i++) {
var warning = warnings[i]
console.log(` ` + warning)
}
console.log()
process.exit(1)
}
}
config
index.js
config資料夾下最主要的檔案就是index.js了,在這裡面描述了開發和構建兩種環境下的配置,前面的build資料夾下也有不少檔案引用了index.js裡面的配置
// see http://vuejs-templates.github.io/webpack for documentation.
var path = require(`path`)
module.exports = {
// 構建產品時使用的配置
build: {
// 環境變數
env: require(`./prod.env`),
// html入口檔案
index: path.resolve(__dirname, `../dist/index.html`),
// 產品檔案的存放路徑
assetsRoot: path.resolve(__dirname, `../dist`),
// 二級目錄,存放靜態資原始檔的目錄,位於dist資料夾下
assetsSubDirectory: `static`,
// 釋出路徑,如果構建後的產品檔案有用於釋出CDN或者放到其他域名的伺服器,可以在這裡進行設定
// 設定之後構建的產品檔案在注入到index.html中的時候就會帶上這裡的釋出路徑
assetsPublicPath: `/`,
// 是否使用source-map
productionSourceMap: true,
// Gzip off by default as many popular static hosts such as
// Surge or Netlify already gzip all static assets for you.
// Before setting to `true`, make sure to:
// npm install --save-dev compression-webpack-plugin
// 是否開啟gzip壓縮
productionGzip: false,
// gzip模式下需要壓縮的檔案的副檔名,設定js、css之後就只會對js和css檔案進行壓縮
productionGzipExtensions: [`js`, `css`],
// Run the build command with an extra argument to
// View the bundle analyzer report after build finishes:
// `npm run build --report`
// Set to `true` or `false` to always turn it on or off
// 是否展示webpack構建打包之後的分析報告
bundleAnalyzerReport: process.env.npm_config_report
},
// 開發過程中使用的配置
dev: {
// 環境變數
env: require(`./dev.env`),
// dev-server監聽的埠
port: 8080,
// 是否自動開啟瀏覽器
autoOpenBrowser: true,
// 靜態資原始檔夾
assetsSubDirectory: `static`,
// 釋出路徑
assetsPublicPath: `/`,
// 代理配置表,在這裡可以配置特定的請求代理到對應的API介面
// 例如將`localhost:8080/api/xxx`代理到`www.example.com/api/xxx`
proxyTable: {},
// CSS Sourcemaps off by default because relative paths are "buggy"
// with this option, according to the CSS-Loader README
// (https://github.com/webpack/css-loader#sourcemaps)
// In our experience, they generally work as expected,
// just be aware of this issue when enabling this option.
// 是否開啟 cssSourceMap
cssSourceMap: false
}
}
`use strict`
const path = require(`path`)
module.exports = {
dev: {
// 開發環境下面的配置
assetsSubDirectory: `static`,//子目錄,一般存放css,js,image等檔案
assetsPublicPath: `/`,//根目錄
proxyTable: {},//可利用該屬性解決跨域的問題
host: `localhost`, // 地址
port: 8080, //埠號設定,埠號佔用出現問題可在此處修改
autoOpenBrowser: false,//是否在編譯(輸入命令列npm run dev)後開啟http://localhost:8080/頁面,以前配置為true,近些版本改為false,個人偏向習慣自動開啟頁面
errorOverlay: true,//瀏覽器錯誤提示
notifyOnErrors: true,//跨平臺錯誤提示
poll: false, //使用檔案系統(file system)獲取檔案改動的通知devServer.watchOptions
devtool: `cheap-module-eval-source-map`,//增加除錯,該屬性為原始原始碼(僅限行)不可在生產環境中使用
cacheBusting: true,//使快取失效
cssSourceMap: true//程式碼壓縮後進行調bug定位將非常困難,於是引入sourcemap記錄壓縮前後的位置資訊記錄,當產生錯誤時直接定位到未壓縮前的位置,將大大的方便我們除錯
},
build: {
// 生產環境下面的配置
index: path.resolve(__dirname, `../dist/index.html`),//index編譯後生成的位置和名字,根據需要改變字尾,比如index.php
assetsRoot: path.resolve(__dirname, `../dist`),//編譯後存放生成環境程式碼的位置
assetsSubDirectory: `static`,//js,css,images存放資料夾名
assetsPublicPath: `/`,//釋出的根目錄,通常本地打包dist後開啟檔案會報錯,此處修改為./。如果是上線的檔案,可根據檔案存放位置進行更改路徑
productionSourceMap: true,
devtool: `#source-map`,//①
//unit的gzip命令用來壓縮檔案,gzip模式下需要壓縮的檔案的副檔名有js和css
productionGzip: false,
productionGzipExtensions: [`js`, `css`],
bundleAnalyzerReport: process.env.npm_config_report
}
}
prod.env.js
當開發是調取dev.env.js的開發環境配置,釋出時呼叫prod.env.js的生產環境配置
`use strict`
module.exports = {
NODE_ENV: `"production"`
}
dev.env.js
config內的檔案其實是服務於build的,大部分是定義一個變數export出去。
`use strict`//採用嚴格模式
const merge = require(`webpack-merge`)//①
const prodEnv = require(`./prod.env`)
//webpack-merge提供了一個合併函式,它將陣列和合並物件建立一個新物件。
//如果遇到函式,它將執行它們,通過演算法執行結果,然後再次將返回的值封裝在函式中.這邊將dev和prod進行合併
module.exports = merge(prodEnv, {
NODE_ENV: `"development"`
})
src
①、assets檔案:腳手架自動會放入一個圖片在裡面作為初始頁面的logo。平常我們使用的時候會在裡面建立js,css,img,fonts等資料夾,作為靜態資源呼叫
②、components資料夾:用來存放元件,合理地使用元件可以高效地實現複用等功能,從而更好地開發專案。一般情況下比如建立頭部元件的時候,我們會新建一個header的資料夾,然後再新建一個header.vue的檔案
③、router資料夾:該資料夾下有一個叫index.js檔案,用於實現頁面的路由跳轉,具體使用請點選→vue-router傳送門
④、App.vue:作為我們的主元件,可通過使用<router-view/>開放入口讓其他的頁面元件得以顯示。
⑤、main.js:作為我們的入口檔案,主要作用是初始化vue例項並使用需要的外掛,小型專案省略router時可放在該處
.babelrc
{
//制定轉碼的規則
"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"]//①
}
.postcessrc.js
.postcssrc.js檔案其實是postcss-loader包的一個配置,在webpack的舊版本可以直接在webpack.config.js中配置,現版本中postcss的文件示例獨立出.postcssrc.js,裡面寫進去需要使用到的外掛
module.exports = {
"plugins": {
"postcss-import": {},//①
"postcss-url": {},//②
"autoprefixer": {}//③
}
}
package.json
package.json來制定名單,需要哪些npm包來參與到專案中來,npm install命令根據這個配置檔案增減來管理本地的安裝包
{
//從name到private都是package的配置資訊,也就是我們在腳手架搭建中輸入的專案描述
"name": "shop",//專案名稱:不能以.(點)或者_(下劃線)開頭,不能包含大寫字母,具有明確的的含義與現有專案名字不重複
"version": "1.0.0",//專案版本號:遵循“大版本.次要版本.小版本”
"description": "A Vue.js project",//專案描述
"author": "qietuniu",//作者名字
"private": true,//是否私有
//scripts中的子項即是我們在控制檯執行的指令碼的縮寫
"scripts": {
//①webpack-dev-server:啟動了http伺服器,實現實時編譯;
//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相同,直接執行開發環境
"build": "node build/build.js"//使用node執行build檔案
},
//②dependencies(專案依賴庫):在安裝時使用--save則寫入到dependencies
"dependencies": {
"vue": "^2.5.2",//vue.js
"vue-router": "^3.0.1"//vue的路由外掛
},
//和devDependencies(開發依賴庫):在安裝時使用--save-dev將寫入到devDependencies
"devDependencies": {
"autoprefixer": "^7.1.2",//autoprefixer作為postcss外掛用來解析CSS補充字首,例如 display: flex會補充為display:-webkit-box;display: -webkit-flex;display: -ms-flexbox;display: flex。
//babel:以下幾個babel開頭的都是針對es6解析的外掛。用最新標準編寫的 JavaScript 程式碼向下編譯成可以在今天隨處可用的版本
"babel-core": "^6.22.1",//babel的核心,把 js 程式碼分析成 ast ,方便各個外掛分析語法進行相應的處理。
"babel-helper-vue-jsx-merge-props": "^2.0.3",//預製babel-template函式,提供給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環境中
"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第二階段的規範
"chalk": "^2.0.1",//用來在命令列輸出不同顏色文字
"copy-webpack-plugin": "^4.0.1",//拷貝資源和檔案
"css-loader": "^0.28.0",//webpack先用css-loader載入器去解析字尾為css的檔案,再使用style-loader生成一個內容為最終解析完的css程式碼的style標籤,放到head標籤裡
"extract-text-webpack-plugin": "^3.0.0",//將一個以上的包裡面的文字提取到單獨檔案中
"file-loader": "^1.1.4",//③打包壓縮檔案,與url-loader用法類似
"friendly-errors-webpack-plugin": "^1.6.1",//識別某些類別的WebPACK錯誤和清理,聚合和優先排序,以提供更好的開發經驗
"html-webpack-plugin": "^2.30.1",//簡化了HTML檔案的建立,引入了外部資源,建立html的入口檔案,可通過此項進行多頁面的配置
"node-notifier": "^5.1.2",//支援使用node傳送跨平臺的本地通知
"optimize-css-assets-webpack-plugin": "^3.2.0",//壓縮提取出的css,並解決ExtractTextPlugin分離出的js重複問題(多個檔案引入同一css檔案)
"ora": "^1.2.0",//載入(loading)的外掛
"portfinder": "^1.0.13",//檢視程式埠
"postcss-import": "^11.0.0",//可以消耗本地檔案、節點模組或web_modules
"postcss-loader": "^2.0.8",//用來相容css的外掛
"postcss-url": "^7.2.1",//URL上重新定位、內聯或複製
"rimraf": "^2.6.0",//節點的UNIX命令RM—RF,強制刪除檔案或者目錄的命令
"semver": "^5.3.0",//用來對特定的版本號做判斷的
"shelljs": "^0.7.6",//使用它來消除shell指令碼在UNIX上的依賴性,同時仍然保留其熟悉和強大的命令,即可執行Unix系統命令
"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動態地注入到文件中作為樣式標籤
"vue-template-compiler": "^2.5.2",//這個包可以用來預編譯VUE模板到渲染函式,以避免執行時編譯開銷和CSP限制
"webpack": "^3.6.0",//打包工具
"webpack-bundle-analyzer": "^2.9.0",//視覺化webpack輸出檔案的大小
"webpack-dev-server": "^2.9.1",//提供一個提供實時過載的開發伺服器
"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"
]
}
註釋:
②、devDependencies和dependencies的區別: devDependencies裡面的外掛只用於開發環境,不用於生產環境,即輔助作用,打包的時候需要,打包完成就不需要了。而dependencies是需要釋出到生產環境的,自始至終都在。比如wepack等只是在開發中使用的包就寫入到devDependencies,而像vue這種專案全程依賴的包要寫入到dependencies
③、file-loader和url-loader的區別:以圖片為例,file-loader可對圖片進行壓縮,但是還是通過檔案路徑進行引入,當http請求增多時會降低頁面效能,而url-loader通過設定limit引數,小於limit位元組的圖片會被轉成base64的檔案,大於limit位元組的將進行圖片壓縮的操作。總而言之,url-loader是file-loader的上層封裝。
點這裡→file-loader 和 url-loader詳解
其他檔案
①、.editorconfig:編輯器的配置檔案
②、.gitignore:忽略git提交的一個檔案,配置之後提交時將不會載入忽略的檔案
③、index.html:頁面入口,經過編譯之後的程式碼將插入到這來。
④、package.lock.json:鎖定安裝時的包的版本號,並且需要上傳到git,以保證其他人在npm install時大家的依賴能保證一致
⑤、README.md:可此填寫專案介紹
⑥、node_modules:根據package.json安裝時候生成的的依賴(安裝包)