摘要:前端開發(fā)過程中需要使用到后臺的的話,可以通過配置來將相應(yīng)的后臺請求代理到專用的服務(wù)器。主要完成下面幾件事情合并基礎(chǔ)的配置配置樣式文件的處理規(guī)則,配置的輸出配置插件模式下的插件配置分析說明插件里面多了丑化壓縮代碼以及抽離文件等插件。
[toc]
目錄 結(jié)構(gòu)預(yù)覽├─build // 保存一些webpack的初始化配置,項(xiàng)目構(gòu)建 │ ├─build.js // 生產(chǎn)環(huán)境構(gòu)建 │ ├─check-version.js // 檢查npm、node版本 │ ├─vue-loader.conf.js // webpack loader配置 │ ├─webpack.base.conf.js// webpack基礎(chǔ)配置 │ ├─webpack.dev.conf.js // 開發(fā)環(huán)境配置,構(gòu)建本地開發(fā)服務(wù)器 │ ├─webpack.prod.conf.js// 生產(chǎn)環(huán)境的配置 │ ├─config // config文件夾保存一些項(xiàng)目初始化的配置 │ ├─dev.env.js // 開發(fā)環(huán)境的配置 │ ├─index.js // 項(xiàng)目一些配置變量 │ ├─prod.env.js // 生產(chǎn)環(huán)境的配置 │ ├─dist // 打包后的項(xiàng)目 ├─node_modules // 依賴包 │ ├─src // 源碼目錄 │ ├─assets // 靜態(tài)文件目錄 │ ├─components // 組件文件 │ ├─router // 路由 │ ├─App.vue // 是項(xiàng)目入口文件 │ ├─main.js // 是項(xiàng)目的核心文件,入口 ├─static // 靜態(tài)資源目錄 ├─.babelrc // Babel的配置文件 ├─.editorconfig // 代碼規(guī)范配置文件 ├─.gitignore // git忽略配置文件 ├─.postcssrc.js // postcss插件配置文件 ├─index.html // 頁面入口文件 ├─package-lock.json // 項(xiàng)目包管控文件 ├─package.json // 項(xiàng)目配置 └─README.md // 項(xiàng)目說明書結(jié)構(gòu)解析 build dev-server.js
首先來看執(zhí)行”npm run dev”時候最先執(zhí)行的build/dev-server.js文件。該文件主要完成下面幾件事情:
檢查node和npm的版本、引入相關(guān)插件和配置
webpack對源碼進(jìn)行編譯打包并返回compiler對象
創(chuàng)建express服務(wù)器
配置開發(fā)中間件(webpack-dev-middleware)和+ 熱重載中間件(webpack-hot-middleware)
掛載代理服務(wù)和中間件
配置靜態(tài)資源
啟動服務(wù)器監(jiān)聽特定端口(8080)
自動打開瀏覽器并打開特定網(wǎng)址(localhost:8080)
說明: express服務(wù)器提供靜態(tài)文件服務(wù),不過它還使用了http-proxy-middleware,一個http請求代理的中間件。前端開發(fā)過程中需要使用到后臺的API的話,可以通過配置proxyTable來將相應(yīng)的后臺請求代理到專用的API服務(wù)器。
// 檢查NodeJS和npm的版本 require("./check-versions")() // 獲取基本配置 var config = require("../config") // 如果Node的環(huán)境變量中沒有設(shè)置當(dāng)前的環(huán)境(NODE_ENV),則使用config中的dev環(huán)境配置作為當(dāng)前的環(huán)境 if (!process.env.NODE_ENV) { process.env.NODE_ENV = JSON.parse(config.dev.env.NODE_ENV) } // opn是一個可以調(diào)用默認(rèn)軟件打開網(wǎng)址、圖片、文件等內(nèi)容的插件 // 這里用它來調(diào)用默認(rèn)瀏覽器打開dev-server監(jiān)聽的端口,例如:localhost:8080 var opn = require("opn") var path = require("path") var express = require("express") var webpack = require("webpack") // http-proxy-middleware是一個express中間件,用于將http請求代理到其他服務(wù)器 // 例:localhost:8080/api/xxx --> localhost:3000/api/xxx // 這里使用該插件可以將前端開發(fā)中涉及到的請求代理到提供服務(wù)的后臺服務(wù)器上,方便與服務(wù)器對接 var proxyMiddleware = require("http-proxy-middleware") // 開發(fā)環(huán)境下的webpack配置 var webpackConfig = require("./webpack.dev.conf") // dev-server 監(jiān)聽的端口,如果沒有在命令行傳入端口號,則使用config.dev.port設(shè)置的端口,例如8080 var port = process.env.PORT || config.dev.port // 用于判斷是否要自動打開瀏覽器的布爾變量,當(dāng)配置文件中沒有設(shè)置自動打開瀏覽器的時候其值為 false var autoOpenBrowser = !!config.dev.autoOpenBrowser // HTTP代理表,指定規(guī)則,將某些API請求代理到相應(yīng)的服務(wù)器 var proxyTable = config.dev.proxyTable // 創(chuàng)建express服務(wù)器 var app = express() // webpack根據(jù)配置開始編譯打包源碼并返回compiler對象 var compiler = webpack(webpackConfig) // webpack-dev-middleware將webpack編譯打包后得到的產(chǎn)品文件存放在內(nèi)存中而沒有寫進(jìn)磁盤 // 將這個中間件掛到express上使用之后即可提供這些編譯后的產(chǎn)品文件服務(wù) var devMiddleware = require("webpack-dev-middleware")(compiler, { publicPath: webpackConfig.output.publicPath, // 設(shè)置訪問路徑為webpack配置中的output里面所對應(yīng)的路徑 quiet: true // 設(shè)置為true,使其不要在控制臺輸出日志 }) // webpack-hot-middleware,用于實(shí)現(xiàn)熱重載功能的中間件 var hotMiddleware = require("webpack-hot-middleware")(compiler, { log: false, // 關(guān)閉控制臺的日志輸出 heartbeat: 2000 // 發(fā)送心跳包的頻率 }) // webpack(重新)編譯打包完成后并將js、css等文件inject到html文件之后,通過熱重載中間件強(qiáng)制頁面刷新 compiler.plugin("compilation", function (compilation) { compilation.plugin("html-webpack-plugin-after-emit", function (data, cb) { hotMiddleware.publish({ action: "reload" }) cb() }) }) // 根據(jù) proxyTable 中的代理請求配置來設(shè)置express服務(wù)器的http代理規(guī)則 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(單頁應(yīng)用) // 例如使用vue-router并開啟了history模式 app.use(require("connect-history-api-fallback")()) // serve webpack bundle output // 掛載webpack-dev-middleware中間件,提供webpack編譯打包后的產(chǎn)品文件服務(wù) app.use(devMiddleware) // enable hot-reload and state-preserving // compilation error display // 掛載熱重載中間件 app.use(hotMiddleware) // serve pure static assets // 提供static文件夾上的靜態(tài)文件服務(wù) var staticPath = path.posix.join(config.dev.assetsPublicPath, config.dev.assetsSubDirectory) app.use(staticPath, express.static("./static")) // 訪問鏈接 var uri = "http://localhost:" + port // 創(chuàng)建promise,在應(yīng)用服務(wù)啟動之后resolve // 便于外部文件require了這個dev-server之后的代碼編寫 var _resolve var readyPromise = new Promise(resolve => { _resolve = resolve }) console.log("> Starting dev server...") // webpack-dev-middleware等待webpack完成所有編譯打包之后輸出提示語到控制臺,表明服務(wù)正式啟動 // 服務(wù)正式啟動才自動打開瀏覽器進(jìn)入頁面 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服務(wù)器并監(jiān)聽相應(yīng)的端口 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文件(測試環(huán)境下使用的是build/webpack.prod.conf.js,這里暫時不考慮測試環(huán)境)。而build/webpack.dev.conf.js中又引用了webpack.base.conf.js,所以這里我先分析webpack.base.conf.js。
webpack.base.conf.js主要完成了下面這些事情:
配置webpack編譯入口
配置webpack輸出路徑和命名規(guī)則
配置模塊resolve規(guī)則
配置不同類型模塊的處理規(guī)則
說明: 這個配置里面只配置了.js、.vue、圖片、字體等幾類文件的處理規(guī)則,如果需要處理其他文件可以在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輸出路徑和命名規(guī)則 output: { // webpack輸出的目標(biāo)文件夾路徑(例如:/dist) path: config.build.assetsRoot, // webpack輸出bundle文件命名格式 filename: "[name].js", // webpack編譯輸出的發(fā)布路徑(例如"http://cdn.xxx.com/app/") publicPath: process.env.NODE_ENV === "production" ? config.build.assetsPublicPath : config.dev.assetsPublicPath }, // 模塊resolve的規(guī)則 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 }, // 不同類型模塊的處理規(guī)則 module: { rules: [ {// 對src和test文件夾下的.js和.vue文件使用eslint-loader進(jìn)行代碼規(guī)范檢查 test: /.(js|vue)$/, loader: "eslint-loader", enforce: "pre", include: [resolve("src"), resolve("test")], options: { formatter: require("eslint-friendly-formatter") } }, {// 對所有.vue文件使用vue-loader進(jìn)行編譯 test: /.vue$/, loader: "vue-loader", options: vueLoaderConfig }, {// 對src和test文件夾下的.js文件使用babel-loader將es6+的代碼轉(zhuǎn)成es5 test: /.js$/, loader: "babel-loader", include: [resolve("src"), resolve("test")] }, {// 對圖片資源文件使用url-loader test: /.(png|jpe?g|gif|svg)(?.*)?$/, loader: "url-loader", options: { // 小于10K的圖片轉(zhuǎn)成base64編碼的dataURL字符串寫到代碼中 limit: 10000, // 其他的圖片轉(zhuǎn)移到靜態(tài)資源文件夾 name: utils.assetsPath("img/[name].[hash:7].[ext]") } }, {// 對多媒體資源文件使用url-loader test: /.(mp4|webm|ogg|mp3|wav|flac|aac)(?.*)?$/, loader: "url-loader", options: { // 小于10K的資源轉(zhuǎn)成base64編碼的dataURL字符串寫到代碼中 limit: 10000, // 其他的資源轉(zhuǎn)移到靜態(tài)資源文件夾 name: utils.assetsPath("media/[name].[hash:7].[ext]") } }, {// 對字體資源文件使用url-loader test: /.(woff2?|eot|ttf|otf)(?.*)?$/, loader: "url-loader", options: { // 小于10K的資源轉(zhuǎn)成base64編碼的dataURL字符串寫到代碼中 limit: 10000, // 其他的資源轉(zhuǎn)移到靜態(tài)資源文件夾 name: utils.assetsPath("fonts/[name].[hash:7].[ext]") } } ] } }webpack.dev.conf.js
接下來看webpack.dev.conf.js,這里面在webpack.base.conf的基礎(chǔ)上增加完善了開發(fā)環(huán)境下面的配置,主要包括下面幾件事情:
將webpack的熱重載客戶端代碼添加到每個entry對應(yīng)的應(yīng)用
合并基礎(chǔ)的webpack配置
配置樣式文件的處理規(guī)則,styleLoaders
配置Source Maps
配置webpack插件
var utils = require("./utils") var webpack = require("webpack") var config = require("../config") // webpack-merge是一個可以合并數(shù)組和對象的插件 var merge = require("webpack-merge") var baseWebpackConfig = require("./webpack.base.conf") // html-webpack-plugin用于將webpack編譯打包后的產(chǎn)品文件注入到html模板中 // 即自動在index.html里面加上和