webpack和gulp

weixin_34162695發表於2017-12-12

webpack:是前端模組化管理和打包工具/模組打包機。它可以將瀏覽器不能直接執行的擴充套件語言(sass,less,script)打包成合適的格式供瀏覽器使用,在瀏覽器使用的,伺服器的node 不使用。 webpack的優勢:打包符合commonjs規範的js檔案, 1:可以將依賴載入成按需架載入的快, 2:初始化載入耗時儘量少。 使用webpack命令打包時,需全域性安裝,基於node的,npm install -g webpack 實現負責功能時,使用配置檔案,本質就是一個物件,配置檔案的名稱是固定的, webpack.config.js,放在專案的根目錄下,module.exports={ //入口檔案 entry:__dirname+"" //出口檔案 output:{ path:__dirname+"/dest", filename:“” } } 結合npm命令使用webpack的話,npm init --yes 結合webpack進行模組化開發:伺服器的node.js可以直接使用commonjs規範,就是module.export和require 瀏覽器端:不支援commonjs規範,也不支援es6的export和import,結合webpack使用就可以支援commonjs規範和es6的export和import vue-cli腳手架中的webpack配置, 目錄結構: ├── README.md ├── build │ ├── build.js │ ├── check-versions.js │ ├── dev-client.js │ ├── dev-server.js │ ├── utils.js │ ├── vue-loader.conf.js │ ├── webpack.base.conf.js │ ├── webpack.dev.conf.js │ └── webpack.prod.conf.js ├── config │ ├── dev.env.js │ ├── index.js │ └── prod.env.js ├── index.html ├── package.json ├── src │ ├── App.vue │ ├── assets │ │ └── logo.png │ ├── components │ │ └── Hello.vue │ └── main.js └── static build目錄下 webpack.base.conf.js 入口檔案 entry entry:{ app:'.src/main.js' } 輸出檔案output config配置在config/index.js檔案中, output:{ path:'config.build.assetsRoot'//匯出目錄的絕對路徑 filename:'[name].js' //匯出檔案的檔名 publicPath: process.env.NODE_ENV === 'production'? config.build.assetsPublicPath : config.dev.assetsPublicPath //生產模式或開發模式下html、js等檔案內部引用的公共路徑 } 檔案解析resolve 主要設定模組如何被解析。

resolve: { extensions: ['.js', '.vue', '.json'], //自動解析確定的擴充名,使匯入模組時不帶擴充名 alias: { // 建立import或require的別名 'vue$': 'vue/dist/vue.esm.js', '@': resolve('src') } } 模版解析module,處理專案不同型別的模組, module: { rules: [ { test: /.vue$/, // vue檔案字尾 loader: 'vue-loader', //使用vue-loader處理 options: vueLoaderConfig //options是對vue-loader做的額外選項配置 }, { test: /.js$/, // js檔案字尾 loader: 'babel-loader', //使用babel-loader處理 include: [resolve('src'), resolve('test')] //必須處理包含src和test資料夾 }, { test: /.(png|jpe?g|gif|svg)(?.)?$/, //圖片字尾 loader: 'url-loader', //使用url-loader處理 query: { // query是對loader做額外的選項配置 limit: 10000, //圖片小於10000位元組時以base64的方式引用 name: utils.assetsPath('img/[name].[hash:7].[ext]') //檔名為name.7位hash值.擴充名 } }, { test: /.(woff2?|eot|ttf|otf)(?.)?$/, //字型檔案 loader: 'url-loader', //使用url-loader處理 query: { limit: 10000, //字型檔案小於1000位元組的時候處理方式 name: utils.assetsPath('fonts/[name].[hash:7].[ext]') //檔名為name.7位hash值.擴充名 } } ] } query 僅由於相容性原因而存在。請使用 options 代替。 webpack.dev.conf.js 開發環境下的webpack配置,通過merge方法合併webpack.base.conf.js基礎配置

var merge = require('webpack-merge') var baseWebpackConfig = require('./webpack.base.conf') module.exports = merge(baseWebpackConfig, {}) 模組配置 module: { //通過傳入一些配置來獲取rules配置,此處傳入了sourceMap: false,表示不生成sourceMap rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap }) } 在util.styleLoaders中的配置如下

exports.styleLoaders = function (options) { var output = [] //定義返回的陣列,陣列中儲存的是針對各型別的樣式檔案的處理方式 var loaders = exports.cssLoaders(options) // 呼叫cssLoaders方法返回各型別的樣式物件(css: loader) for (var extension in loaders) { //迴圈遍歷loaders var loader = loaders[extension] //根據遍歷獲得的key(extension)來得到value(loader) output.push({ // test: new RegExp('\.' + extension + '$'), // 處理的檔案型別 use: loader //用loader來處理,loader來自loaders[extension] }) } return output } 呼叫exports.cssLoaders(options),用來返回針對各型別的樣式檔案的處理方式,具體實現如下

exports.cssLoaders = function (options) { options = options || {}

var cssLoader = { loader: 'css-loader', options: { //options是loader的選項配置 minimize: process.env.NODE_ENV === 'production', //生成環境下壓縮檔案 sourceMap: options.sourceMap //根據引數是否生成sourceMap檔案 } } function generateLoaders (loader, loaderOptions) { //生成loader var loaders = [cssLoader] // 預設是css-loader if (loader) { // 如果引數loader存在 loaders.push({ loader: loader + '-loader', options: Object.assign({}, loaderOptions, { //將loaderOptions和sourceMap組成一個物件 sourceMap: options.sourceMap }) }) } if (options.extract) { // 如果傳入的options存在extract且為true return ExtractTextPlugin.extract({ //ExtractTextPlugin分離js中引入的css檔案 use: loaders, //處理的loader fallback: 'vue-style-loader' //沒有被提取分離時使用的loader }) } else { return ['vue-style-loader'].concat(loaders) } } return { //返回css型別對應的loader組成的物件 generateLoaders()來生成loader css: generateLoaders(), postcss: generateLoaders(), less: generateLoaders('less'), sass: generateLoaders('sass', { indentedSyntax: true }), scss: generateLoaders('sass'), stylus: generateLoaders('stylus'), styl: generateLoaders('stylus') } } 外掛配置 plugins: [ new webpack.DefinePlugin({ // 編譯時配置的全域性變數 'process.env': config.dev.env //當前環境為開發環境 }), new webpack.HotModuleReplacementPlugin(), //熱更新外掛 new webpack.NoEmitOnErrorPlugin(), //不觸發錯誤,即編譯後執行的包正常執行 new HtmlWebpackPlugin({ //自動生成html檔案,比如編譯後檔案的引入 filename: 'index.html', //生成的檔名 template: 'index.html', //模板 inject: true }), new FriendlyErrorsPlugin() //友好的錯誤提示 ] webpack.prod.conf.js 生產環境下的webpack配置,通過merge方法合併webpack.base.conf.js基礎配置

module的處理,主要是針對css的處理 同樣的此處呼叫了utils.styleLoaders

module: { rules: utils.styleLoaders({ sourceMap: config.build.productionSourceMap, extract: true }) } 輸出檔案output output: { //匯出檔案目錄 path: config.build.assetsRoot, //匯出的檔名 filename: utils.assetsPath('js/[name].[chunkhash].js'), //非入口檔案的檔名,而又需要被打包出來的檔案命名配置,如按需載入的模組 chunkFilename: utils.assetsPath('js/[id].[chunkhash].js') } 外掛plugins 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') var CopyWebpackPlugin = require('copy-webpack-plugin') var HtmlWebpackPlugin = require('html-webpack-plugin') var ExtractTextPlugin = require('extract-text-webpack-plugin') var OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin') var env = config.build.env plugins: [ new webpack.DefinePlugin({ 'process.env': env //配置全域性環境為生產環境 }), new webpack.optimize.UglifyJsPlugin({ //js檔案壓縮外掛 compress: { //壓縮配置 warnings: false // 不顯示警告 }, sourceMap: true //生成sourceMap檔案 }), new ExtractTextPlugin({ //將js中引入的css分離的外掛 filename: utils.assetsPath('css/[name].[contenthash].css') //分離出的css檔名 }), //壓縮提取出的css,並解決ExtractTextPlugin分離出的js重複問題(多個檔案引入同一css檔案) new OptimizeCSSPlugin(), //生成html的外掛,引入css檔案和js檔案 new HtmlWebpackPlugin({ filename: config.build.index, //生成的html的檔名 template: 'index.html', //依據的模板 inject: true, //注入的js檔案將會被放在body標籤中,當值為'head'時,將被放在head標籤中 minify: { //壓縮配置 removeComments: true, //刪除html中的註釋程式碼 collapseWhitespace: true, //刪除html中的空白符 removeAttributeQuotes: true //刪除html元素中屬性的引號 }, chunksSortMode: 'dependency' //按dependency的順序引入 }), //分離公共js到vendor中 new webpack.optimize.CommonsChunkPlugin({ name: 'vendor', //檔名 minChunks: functions(module, count) { // 宣告公共的模組來自node_modules資料夾 return (module.resource && /.js$/.test(module.resource) && module,resource.indexOf(path.join(__dirname, '../node_modules')) === 0) } }), //上面雖然已經分離了第三方庫,每次修改編譯都會改變vendor的hash值,導致瀏覽器快取失效。原因是vendor包含了webpack在打包過程中會產生一些執行時程式碼,執行時程式碼中實際上儲存了打包後的檔名。當修改業務程式碼時,業務程式碼的js檔案的hash值必然會改變。一旦改變必然會導致vendor變化。vendor變化會導致其hash值變化。 //下面主要是將執行時程式碼提取到單獨的manifest檔案中,防止其影響vendor.js new webpack.optimize.CommonsChunkPlugin({ name: 'mainifest', chunks: ['vendor'] }), // 複製靜態資源,將static檔案內的內容複製到指定資料夾 new CopyWebpackPlugin([{ from: path.resolve(__dirname, '../static'), to: config.build.assetsSubDirectory, ignore: ['.*'] //忽視.*檔案 }]) ] 額外配置 if (config.build.productionGzip) { //配置檔案開啟了gzip壓縮

//引入壓縮檔案的元件,該外掛會對生成的檔案進行壓縮,生成一個.gz檔案 var CompressionWebpackPlugin = require('compression-webpack-plugin')

webpackConfig.plugins.push( new CompressionWebpackPlugin({ asset: '[ yunpath].gz[query]', //目標檔名 algorithm: 'gzip', //使用gzip壓縮 test: new RegExp( //滿足正規表示式的檔案會被壓縮 '\.(' + config.build.productionGzipExtensions.join('|') + ')$' ), threshold: 10240, //資原始檔大於10240B=10kB時會被壓縮 minRatio: 0.8 //最小壓縮比達到0.8時才會被壓縮 }) ) } 執行 npm run dev 命令時 在package.json檔案中定義了dev執行的指令碼

"scripts": { "dev": "node build/dev-server.js", "build": "node build/build.js" }, 當執行npm run dev命令時,實際上會執行dev-server.js檔案 該檔案以express作為後端框架

// nodejs環境配置 var config = require('../config') if (!process.env.NODE_ENV) { process.env.NODE_ENV = JSON.parse(config.dev.env.NODE_ENV) } var opn = require('opn') //強制開啟瀏覽器 var path = require('path') var express = require('express')//webpack-dev-middleware的作用 //1.將編譯後的生成的靜態檔案放在記憶體中,所以在npm run dev後磁碟上不會生成檔案 //2.當檔案改變時,會自動編譯。 //3.當在編譯過程中請求某個資源時,webpack-dev-server不會讓這個請求失敗,而是會一直阻塞它,直到webpack編譯完畢 var devMiddleware = require('webpack-dev-middleware')(compiler, { publicPath: webpackConfig.output.publicPath, quiet: true })

//webpack-hot-middleware的作用就是實現瀏覽器的無重新整理更新 var hotMiddleware = require('webpack-hot-middleware')(compiler, { log: () => {} }) //宣告hotMiddleware無重新整理更新的時機:html-webpack-plugin 的template更改之後 compiler.plugin('compilation', function (compilation) { compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) { hotMiddleware.publish({ action: 'reload' }) cb() }) })

//將代理請求的配置應用到express服務上 Object.keys(proxyTable).forEach(function (context) { var options = proxyTable[context] if (typeof options === 'string') { options = { target: options } } app.use(proxyMiddleware(options.filter || context, options)) })

//使用connect-history-api-fallback匹配資源 //如果不匹配就可以重定向到指定地址 app.use(require('connect-history-api-fallback')())

// 應用devMiddleware中介軟體 app.use(devMiddleware) // 應用hotMiddleware中介軟體 app.use(hotMiddleware)

// 配置express靜態資源目錄 var staticPath = path.posix.join(config.dev.assetsPublicPath, config.dev.assetsSubDirectory) app.use(staticPath, express.static('./static'))

var uri = 'http://localhost:' + port /編譯成功後列印uri devMiddleware.waitUntilValid(function () { console.log('> Listening at ' + uri + '\n') }) //啟動express服務 module.exports = app.listen(port, function (err) { if (err) { console.log(err) return } // 滿足條件則自動開啟瀏覽器 if (autoOpenBrowser && process.env.NODE_ENV !== 'testing') { opn(uri) } })

var webpack = require('webpack') var proxyMiddleware = require('http-proxy-middleware') //使用代理的中介軟體 var webpackConfig = require('./webpack.dev.conf') //webpack的配置

var port = process.env.PORT || config.dev.port //埠號 var autoOpenBrowser = !!config.dev.autoOpenBrowser //是否自動開啟瀏覽器 var proxyTable = config.dev.proxyTable //http的代理url

var app = express() //啟動express var compiler = webpack(webpackConfig) //webpack編譯 //webpack-dev-middleware的作用 //1.將編譯後的生成的靜態檔案放在記憶體中,所以在npm run dev後磁碟上不會生成檔案 //2.當檔案改變時,會自動編譯。 //3.當在編譯過程中請求某個資源時,webpack-dev-server不會讓這個請求失敗,而是會一直阻塞它,直到webpack編譯完畢 var devMiddleware = require('webpack-dev-middleware')(compiler, { publicPath: webpackConfig.output.publicPath, quiet: true })

//webpack-hot-middleware的作用就是實現瀏覽器的無重新整理更新 var hotMiddleware = require('webpack-hot-middleware')(compiler, { log: () => {} }) //宣告hotMiddleware無重新整理更新的時機:html-webpack-plugin 的template更改之後 compiler.plugin('compilation', function (compilation) { compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) { hotMiddleware.publish({ action: 'reload' }) cb() }) })

//將代理請求的配置應用到express服務上 Object.keys(proxyTable).forEach(function (context) { var options = proxyTable[context] if (typeof options === 'string') { options = { target: options } } app.use(proxyMiddleware(options.filter || context, options)) })

//使用connect-history-api-fallback匹配資源 //如果不匹配就可以重定向到指定地址 app.use(require('connect-history-api-fallback')())

// 應用devMiddleware中介軟體 app.use(devMiddleware) // 應用hotMiddleware中介軟體 app.use(hotMiddleware)

// 配置express靜態資源目錄 var staticPath = path.posix.join(config.dev.assetsPublicPath, config.dev.assetsSubDirectory) app.use(staticPath, express.static('./static'))

var uri = 'http://localhost:' + port /編譯成功後列印uri devMiddleware.waitUntilValid(function () { console.log('> Listening at ' + uri + '\n') }) //啟動express服務 module.exports = app.listen(port, function (err) { if (err) { console.log(err) return } // 滿足條件則自動開啟瀏覽器 if (autoOpenBrowser && process.env.NODE_ENV !== 'testing') { opn(uri) } }) 執行npm run build 由於package.json中的配置,執行此命令後會執行build.js檔案

process.env.NODE_ENV = 'production' //設定當前環境為production var ora = require('ora') //終端顯示的轉輪loading var rm = require('rimraf') //node環境下rm -rf的命令庫 var path = require('path') //檔案路徑處理庫 var chalk = require('chalk') //終端顯示帶顏色的文字 var webpack = require('webpack') var config = require('../config') var webpackConfig = require('./webpack.prod.conf') //生產環境下的webpack配置

// 在終端顯示ora庫的loading效果 var spinner = ora('building for production...') spinner.start()

// 刪除已編譯檔案 rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => { if (err) throw err //在刪除完成的回撥函式中開始編譯 webpack(webpackConfig, function (err, stats) { spinner.stop() //停止loading if (err) throw err

// 在編譯完成的回撥函式中,在終端輸出編譯的檔案
process.stdout.write(stats.toString({
  colors: true,
  modules: false,
  children: false,
  chunks: false,
  chunkModules: false
}) + '\n\n')
複製程式碼

}) }) gulp:更趨向於開發完後一些自動化處理,優化,可以做js壓縮,css壓縮,less編譯替代手工實現自動化工作

相關文章