vue-cli是構建vue單頁應用的腳手架,輸入一串指定的命令行從而自動生成vue.js+wepack的項目模板。這其中webpack發揮了很大的做用,它使得咱們的代碼模塊化,引入一些插件幫咱們完善功能能夠將文件打包壓縮,圖片轉base64等。後期對項目的配置使得咱們對於腳手架自動生成的代碼的理解更爲重要,接下來我將基於webpack3.6.0版本結合文檔將文件各個擊破,純乾料。php
項目做爲一個你們庭,每一個文件都各司其職。package.json來制定名單,須要哪些npm包來參與到項目中來,npm install命令根據這個配置文件增減來管理本地的安裝包。css
{
html
//從name到private都是package的配置信息,也就是咱們在腳手架搭建中輸入的項目描述
前端
"name": "shop",//項目名稱:不能以.(點)或者_(下劃線)開頭,不能包含大寫字母,具備明確的的含義與現有項目名字不重複
vue
"version": "1.0.0",//項目版本號:遵循「大版本.次要版本.小版本」
html5
"description": "A Vue.js project",//項目描述
node
"author": "qietuniu",//做者名字
webpack
"private": true,//是否私有
git
//scripts中的子項便是咱們在控制檯運行的腳本的縮寫
es6
"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這種項目全程依賴的包要寫入到devDependencies。
二、file-loader和url-loader的區別:以圖片爲例,file-loader可對圖片進行壓縮,可是仍是經過文件路徑進行引入,當http請求增多時會下降頁面性能,而url-loader經過設定limit參數,小於limit字節的圖片會被轉成base64的文件,大於limit字節的將進行圖片壓縮的操做。總而言之,url-loader是file-loader的上層封裝。
.postcssrc.js文件實際上是postcss-loader包的一個配置,在webpack的舊版本能夠直接在webpack.config.js中配置,現版本中postcss的文檔示例獨立出.postcssrc.js,裏面寫進去須要使用到的插件。
module.exports = {
"plugins": {
"postcss-import": {},
"postcss-url": {},
"autoprefixer": {}
}
}
該文件是es6解析的一個配置。
{
//制定轉碼的規則
"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目錄下,根據須要咱們一般會再建一些文件夾。好比pages的文件夾,用來存放頁面讓components文件夾專門作好組件的工做;api文件夾,來封裝請求的參數和方法;store文件夾,使用vuex來做爲vue的狀態管理工具,我也常叫它做前端的數據庫等。
①、assets文件:腳手架自動回放入一個圖片在裏面做爲初始頁面的logo。日常咱們使用的時候會在裏面創建js,css,img,fonts等文件夾,做爲靜態資源調用
②、components文件夾:用來存放組件,合理地使用組件能夠高效地實現複用等功能,從而更好地開發項目。通常狀況下好比建立頭部組件的時候,咱們會新建一個header的文件夾,而後再新建一個header.vue的文件夾
③、router文件夾:該文件夾下有一個叫index.js文件,用於實現頁面的路由跳轉
④、App.vue:做爲咱們的主組件,可經過使用開放入口讓其餘的頁面組件得以顯示。
⑤、main.js:做爲咱們的入口文件,主要做用是初始化vue實例並使用須要的插件,小型項目省略router時可放在該處
①、.editorconfig:編輯器的配置文件
②、.gitignore:忽略git提交的一個文件,配置以後提交時將不會加載忽略的文件
③、index.html:頁面入口,通過編譯以後的代碼將插入到這來。
④、package.lock.json:鎖定安裝時的包的版本號,而且須要上傳到git,以保證其餘人在npm install時你們的依賴能保證一致
⑤、README.md:可此填寫項目介紹
⑥、node_modules:根據package.json安裝時候生成的的依賴(安裝包)
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"'
})
當開發是調取dev.env.js的開發環境配置,發佈時調用prod.env.js的生產環境配置。
'use strict'
module.exports = {
NODE_ENV: '"production"'
}
'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
}
}
該文件做用,即構建生產版本。package.json中的scripts的build就是node build/build.js,輸入命令行npm run build對該文件進行編譯生成生產環境的代碼。
'use strict'
require('./check-versions')()//check-versions:調用檢查版本的文件。加()表明直接調用該函數
process.env.NODE_ENV = 'production'//設置當前是生產環境
//下面定義常量引入插件
const ora = require('ora')//①加載動畫
const rm = require('rimraf')//②刪除文件
const path = require('path')
const chalk = require('chalk')//③對文案輸出的一個彩色設置
const webpack = require('webpack')
const config = require('../config')//默認讀取下面的index.js文件
const webpackConfig = require('./webpack.prod.conf')
//調用start的方法實現加載動畫,優化用戶體驗
const spinner = ora('building for production...')
spinner.start()
//先刪除dist文件再生成新文件,由於有時候會使用hash來命名,刪除整個文件可避免冗餘
rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
if (err) throw err
webpack(webpackConfig, (err, stats) => {
spinner.stop()
if (err) throw err
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')
if (stats.hasErrors()) {
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'
))
})
})
該文件用於檢測node和npm的版本,實現版本依賴
utils是工具的意思,是一個用來處理css的文件。
註釋:
path.posix:提供對路徑方法的POSIX(可移植性操做系統接口)特定實現的訪問,便可跨平臺,區別於win32。
path.join:用於鏈接路徑,會正確使用當前系統的路徑分隔符,Unix系統是"/",Windows系統是"\"
該文件的主要做用就是處理.vue文件,解析這個文件中的每一個語言塊(template、script、style),轉換成js可用的js模塊。
webpack.base.conf.js是開發和生產共同使用提出來的基礎配置文件,主要實現配製入口,配置輸出環境,配置模塊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)
}
module.exports = {
//path.join將路徑片斷進行拼接,而path.resolve將以/開始的路徑片斷做爲根目錄,在此以前的路徑將會被丟棄
//path.join('/a', '/b') // 'a/b',path.resolve('/a', '/b') // '/b'
context: path.resolve(__dirname, '../'),
//配置入口,默認爲單頁面因此只有app一個入口
entry: {
app: './src/main.js'
},
//配置出口,默認是/dist做爲目標文件夾的路徑
output: {
path: config.build.assetsRoot,//路徑
filename: '[name].js',//文件名
publicPath: process.env.NODE_ENV === 'production'
? config.build.assetsPublicPath
: config.dev.assetsPublicPath//公共存放路徑
},
resolve: {
//自動的擴展後綴,好比一個js文件,則引用時書寫可不要寫.js
extensions: ['.js', '.vue', '.json'],
//建立路徑的別名,好比增長'components': resolve('src/components')等
alias: {
'vue$': 'vue/dist/vue.esm.js',
'@': resolve('src'),
}
},
//使用插件配置相應文件的處理方法
module: {
rules: [
//使用vue-loader將vue文件轉化成js的模塊①
{
test: /\.vue$/,
loader: 'vue-loader',
options: vueLoaderConfig
},
//js文件須要經過babel-loader進行編譯成es5文件以及壓縮等操做②
{
test: /\.js$/,
loader: 'babel-loader',
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: 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,
dgram: 'empty',
fs: 'empty',
net: 'empty',
tls: 'empty',
child_process: 'empty'
}
}
'use strict'
const utils = require('./utils')
const webpack = require('webpack')
const config = require('../config')
//經過webpack-merge實現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')
const portfinder = require('portfinder')// 查看空閒端口位置,默認狀況下搜索8000這個端口②
const HOST = process.env.HOST//③processs爲node的一個全局對象獲取當前程序的環境變量,即host
const PORT = process.env.PORT && Number(process.env.PORT)
const devWebpackConfig = merge(baseWebpackConfig, {
module: {
//規則是工具utils中處理出來的styleLoaders,生成了css,less,postcss等規則
rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true })
},
devtool: config.dev.devtool, //加強調試,上文有說起
//此處的配置都是在config的index.js中設定好了
devServer: {//④
clientLogLevel: 'warning',//控制檯顯示的選項有none, error, warning 或者 info
//當使用 HTML5 History API 時,任意的 404 響應均可能須要被替代爲 index.html
historyApiFallback: {
rewrites: [
{ from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') },
],
},
hot: true,//熱加載
contentBase: false,
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')
}),
new webpack.HotModuleReplacementPlugin(),//⑤模塊熱替換插件,修改模塊時不須要刷新頁面
new webpack.NamedModulesPlugin(), // 顯示文件的正確名字
new webpack.NoEmitOnErrorsPlugin(),//當webpack編譯錯誤的時候,來中端打包進程,防止錯誤代碼打包到文件中
// https://github.com/ampedandwired/html-webpack-plugin
// 該插件可自動生成一個 html5 文件或使用模板文件將編譯好的代碼注入進去⑥
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 {
//端口被佔用時就從新設置evn和devServer的端口
process.env.PORT = port
devWebpackConfig.devServer.port = port
//友好地輸出信息
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)
}
})
})
'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
}),
new UglifyJsPlugin({
uglifyOptions: {
compress: {//壓縮
warnings: false//警告:true保留警告,false不保留
}
},
sourceMap: config.build.productionSourceMap,
parallel: true
}),
new ExtractTextPlugin({//抽取文本。好比打包以後的index頁面有style插入,就是這個插件抽取出來的,減小請求
filename: utils.assetsPath('css/[name].[contenthash].css'),
allChunks: true,
}),
new OptimizeCSSPlugin({//優化css的插件
cssProcessorOptions: config.build.productionSourceMap
? { safe: true, map: { inline: false } }
: { safe: true }
}),
new HtmlWebpackPlugin({//html打包
filename: config.build.index,
template: 'index.html',
inject: true,
minify: {//壓縮
removeComments: true,//刪除註釋
collapseWhitespace: true,//刪除空格
removeAttributeQuotes: true//刪除屬性的引號
},
chunksSortMode: 'dependency'//模塊排序,按照咱們須要的順序排序
}),
new webpack.HashedModuleIdsPlugin(),
new webpack.optimize.ModuleConcatenationPlugin(),
new webpack.optimize.CommonsChunkPlugin({//抽取公共的模塊
name: 'vendor',
minChunks (module) {
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
}),
new CopyWebpackPlugin([//複製,好比打包完以後須要把打包的文件複製到dist裏面
{
from: path.resolve(__dirname, '../static'),
to: config.build.assetsSubDirectory,
ignore: ['.*']
}
])
]
})
if (config.build.productionGzip) {
const 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
})
)
}
if (config.build.bundleAnalyzerReport) {
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
webpackConfig.plugins.push(new BundleAnalyzerPlugin())
}
module.exports = webpackConfig