前言
創(chuàng)新互聯(lián)建站成都網(wǎng)站建設(shè)按需設(shè)計(jì),是成都網(wǎng)站制作公司,為垃圾桶提供網(wǎng)站建設(shè)服務(wù),有成熟的網(wǎng)站定制合作流程,提供網(wǎng)站定制設(shè)計(jì)服務(wù):原型圖制作、網(wǎng)站創(chuàng)意設(shè)計(jì)、前端HTML5制作、后臺程序開發(fā)等。成都網(wǎng)站建設(shè)熱線:18982081108
在對著產(chǎn)品高舉中指怒發(fā)心中之憤后,真正能夠解決問題的是自身上的改變,有句話說的好:你雖然改變不了全世界,但是你有機(jī)會改變你自己。秉承著“不聽老人言,吃虧在眼前”的優(yōu)良作風(fēng),我還是決定玩火自焚。
問題所在
之前的項(xiàng)目總結(jié)為以下內(nèi)容:
1、AMD模塊規(guī)范開發(fā),使用requirejs實(shí)現(xiàn),使用rjs打包,最終導(dǎo)致的結(jié)果是,輸出的項(xiàng)目臃腫,腫的就像一坨狗不理……不忍直視
2、使用gulp進(jìn)行打包,這一點(diǎn)貌似沒有可吐槽的地方,畢竟都是被grunt折磨過來的……
3、數(shù)據(jù)的渲染使用模板引擎,這就意味著你要手動管理DOM,這樣,你的業(yè)務(wù)代碼參雜著你的數(shù)據(jù)處理、DOM管理,滿屏幕的毛線……
4、模塊化不足,雖然使用require進(jìn)行了模塊管理,但是大部分業(yè)務(wù)邏輯還是充斥在一個文件里,這與最近流行的組件化概念冰火不容,拒絕落后……
5、諸如 擴(kuò)展性 、 維護(hù)性 我想早已不言而喻,不需贅述,再述就真TM是累贅了。
新框架要解決的問題:
1、要使構(gòu)建輸出的項(xiàng)目像你鄰家小妹妹一樣、瘦的皮包骨。(也許是營養(yǎng)不良)
2、要實(shí)現(xiàn)真正的模塊化、組件化的開發(fā)方式,真正去解決維護(hù)難、擴(kuò)展難的問題。(從此不怕產(chǎn)品汪)
3、業(yè)務(wù)邏輯專注數(shù)據(jù)處理,手動管理DOM的年代就像……像什么呢?(畢竟成人用品也越來越自動化了)
4、等等…….(其實(shí)好處無需贅述,來,往下看)
為了達(dá)成以上目標(biāo),我們探討一下解決方案:
1、老項(xiàng)目的構(gòu)建輸出為什么臃腫?
答:因?yàn)槭褂玫氖莚equire的rjs進(jìn)行構(gòu)建打包的,了解rjs的都知道,它會把項(xiàng)目所有依賴都打包在一個文件里,如果項(xiàng)目中有很多頁面依賴這個模塊,那么rjs并不會把這個模塊提取出來作為公共模塊,所以就會有很多復(fù)制性的內(nèi)容,所以項(xiàng)目自然臃腫。
解決方案:使用webpack配合相應(yīng)的loader,來完成模塊加載和構(gòu)建的工作。
2、老項(xiàng)目為什么模塊化的不足?
答:老項(xiàng)目的模塊化,僅僅體現(xiàn)在js層面,解決了模塊引用的問題,但在開發(fā)方式上,依然可以看做是過程式的,這樣的結(jié)果就導(dǎo)致了項(xiàng)目的難擴(kuò)展和難維護(hù),讓開發(fā)人員在與產(chǎn)品汪的對峙中,并不從容。
解決方案:Vue.js能夠很好的解決組件化的問題,配合 Vue.js 官方提供的 vue-loader 能夠很好的結(jié)合webpack做組件化的開發(fā)架構(gòu)。
3、如何避免手動管理DOM?
答:如果你在做數(shù)據(jù)展示這一塊的開發(fā)工作,相信你一定體會頗深,發(fā)送http請求到服務(wù)端,拿到返回的數(shù)據(jù)后手動渲染DOM至頁面,這是最原始的開發(fā)方式,無非再加一個模板引擎之類的,但最終還是避免不了手動渲染,如果頁面邏輯復(fù)雜,比如給你來一個翻頁的功能,再來一個篩選項(xiàng),估計(jì)你會覺得世界并不那么美好。
解決方案:MVVM模式能夠很好的解決這個問題,而Vue.js的核心也是MVVM。
webpack
你肯定聽說過webpack,如果直接對你描述什么是webpack你可能感受不到他的好處,那么在這之前,我相信你肯定使用過gulp或者grunt,如果你沒使用過也可以,至少你要聽說過并且知道gulp和grunt是干什么的,假如這個你還不清楚,那么你并不是一個合格的前端開發(fā)人員,這篇文章也不適合你,你可以從基礎(chǔ)的地方慢慢學(xué)起。
gulp和grunt對于每一個前端開發(fā)人員應(yīng)該是不陌生的,它們?yōu)榍岸颂峁┝俗詣踊瘶?gòu)建的能力,并且有自己的生態(tài)圈,有很多插件,使得我們告別刀耕火種的時代,但是它們并沒有解決模塊加載的問題,比如我們之前的項(xiàng)目是使用gulp構(gòu)建的,但是模塊化得工作還是要靠require和rjs來完成,而gulp除了完成一些其他任務(wù)之外,就變成了幫助我們免除手動執(zhí)行命令的工具了,別無它用。
而webpack就不同了,webpack的哲學(xué)是一切皆是模塊,無論是js/css/sass/img/coffeejs/ttf….等等,webpack可以使用自定義的loader去把一切資源當(dāng)做模塊加載,這樣就解決了模塊依賴的問題,同時,利用插件還可以對項(xiàng)目進(jìn)行優(yōu)化,由于模塊的加載和項(xiàng)目的構(gòu)建優(yōu)化都是通過webpack一個”人“來解決的,所以模塊的加載和項(xiàng)目的構(gòu)建優(yōu)化并不是無機(jī)分離的,而是有機(jī)的結(jié)合在一起的,是一個組合的過程,這使得webpack在這方面能夠完成的更出色,這也是webpack的優(yōu)勢所在。
如果你看不懂上面的描述,沒關(guān)系,你只需要知道一下幾點(diǎn):
1、過去使用require和rjs等進(jìn)行模塊加載的方式,可以替換為webpack提供的指定loader去完成,你也可以自己開發(fā)加載特定資源的loader。
2、過去使用gulp和grunt完成項(xiàng)目構(gòu)建優(yōu)化的方式,可以替換成webpack提供的插件和特定的配置去完成。
3、由于模塊的加載和項(xiàng)目的構(gòu)建優(yōu)化有機(jī)的結(jié)合,所以webpack能夠更好的完成這項(xiàng)工作
4、并不是說有了webpack就淘汰的gulp等,有些特定的任務(wù),還是要使用gulp去自定義完成的。但是不保證webpack的未來發(fā)展趨勢會怎么樣。
Vue.js
Vue.js是一個MVVM模式的框架,如果讀者有angular經(jīng)驗(yàn),一定能夠很快入門Vue的,那么問題來了,為什么使用Vue而不用angular,
首先,Vue的體積小,輕量在移動端開發(fā)始終是一個不可忽略的話題,其次,Vue在實(shí)現(xiàn)上與angular有本質(zhì)的區(qū)別,讀者可以通過下面兩個鏈接來了解:
1、Vue的變化追蹤和計(jì)算屬性的區(qū)別等
2、Vue 與 angular 及 react 等框架的對比
3、第三點(diǎn)就是Vue提供了webpack的loader —-> [vue-loader],使用它可以讓項(xiàng)目的組件化思想更加清晰
綜上所述,這就是選用Vue的原因
npm 和 nodejs
npm 的全稱是 nodejs包管理,現(xiàn)在越來越多的項(xiàng)目(包)都可以通過npm來安裝管理,nodejs是js運(yùn)行在服務(wù)器端的平臺,它使得js的能力進(jìn)一步提高,我們還要使用nodejs配合 webpack 來完成熱加載的功能。所以讀者最好有nodejs的開發(fā)經(jīng)驗(yàn),如果有express的經(jīng)驗(yàn)更好。
讓我們一步一步從零搭建這個項(xiàng)目
首先新建一個目錄,名為 myProject ,這是我們的項(xiàng)目目錄。然后執(zhí)行一些基本的步驟,比如 npm init 命令,在我們的項(xiàng)目中生成 package.json 文件,這幾乎是必選的,因?yàn)槲覀兊捻?xiàng)目要有很多依賴,都是通過npm來管理的,而npm對于我們項(xiàng)目的管理,則是通過package.json文件:
npm init
執(zhí)行npm init之后,會提示你填寫一些項(xiàng)目的信息,一直回車默認(rèn)就好了,或者直接執(zhí)行 npm init -y 直接跳過詢問步驟
然后我們新建一個叫做 app 的目錄,這個是我們頁面模塊的目錄,再在app目錄下建立一個index目錄,假設(shè)這個是首頁模塊的目錄,然后再在index目錄下建立一個 index.html 文件和 index.js 文件,分別是首頁入口html文件和主js文件,然后再在index目錄下建立一個components目錄,這個目錄用作存放首頁組件模塊的目錄,因?yàn)槲覀冏罱K要實(shí)現(xiàn)組件化開發(fā)。這樣,當(dāng)你完成上面的步驟后,你的項(xiàng)目看上去應(yīng)該是這樣的:
接下來通過npm安裝項(xiàng)目依賴項(xiàng):
npm install\ webpack webpack-dev-server\ vue-loader vue-html-loader css-loader vue-style-loader vue-hot-reload-api\ babel-loader babel-core babel-plugin-transform-runtime babel-preset-es2015\ babel-runtime@5\ --save-dev npm install vue --save
這個時候,你的package.json文件看起來應(yīng)該是這樣的:
"devDependencies": { "babel-core": "^6.3.17", "babel-loader": "^6.2.0", "babel-plugin-transform-runtime": "^6.3.13", "babel-preset-es2015": "^6.3.13", "babel-runtime": "^5.8.34", "css-loader": "^0.23.0", "vue-hot-reload-api": "^1.2.2", "vue-html-loader": "^1.0.0", "vue-style-loader": "^1.0.0", "vue-loader": "^7.2.0", "webpack": "^1.12.9", "webpack-dev-server": "^1.14.0" }, "dependencies": { "vue": "^1.0.13" },
我們安裝了 babel 一系列包,用來解析ES6語法,因?yàn)槲覀兪褂肊S6來開發(fā)項(xiàng)目,如果你不了解ES6語法,建議你看一看阮老師的教程,然后我們安裝了一些loader包,比如css-loader/vue-loader等等,因?yàn)閣ebpack是使用這些指定的loader去加載指定的文件的。
另外我們還使用 npm install vue –save 命令安裝了 vue ,這個就是我們要在項(xiàng)目中使用的vue.js,我們可以直接像開發(fā)nodejs應(yīng)用一樣,直接require(‘vue');即可,而不需要通過script標(biāo)簽引入,這一點(diǎn)在開發(fā)中很爽。
安裝完了依賴,編輯以下文件并保存到相應(yīng)位置:
1、index.html文件:
<!DOCTYPE html> <html lang="zh"> <head> <meta name="viewport" content="width=device-width, height=device-height, initial-scale=1.0, user-scalable=no"> <meta charset="utf-8"> <title>首頁</title> </head> <body> <!-- vue的組件以自定義標(biāo)簽的形式使用 --> <favlist></favlist> </body> </html>
2、index.js文件:
import Vue from 'Vue' import Favlist from './components/Favlist' new Vue({ el: 'body', components: { Favlist } })
3、在components目錄下新建一個 Favlist.vue 文件,作為我們的第一個組件:
<template> <div v-for="n in 10">div</div> </template> <script> export default { data () { return { msg: 'Hello World!' } } } </script> <style> html{ background: red; } </style>
要看懂上面的代碼,你需要了解vue.js,假如你看不懂也沒關(guān)系,我們首先在index.html中使用了自定義標(biāo)簽(即組件),然后在index.js中引入了Vue和我們的Favlist.vue組件,F(xiàn)avlist.vue文件中,我們使用了基本的vue組件語法,最后,我們希望它運(yùn)行起來,這個時候,我們就需要webpack了。
在項(xiàng)目目錄下新建 build 目錄,用來存放我們的構(gòu)建相關(guān)的代碼文件等,然后在build目錄下新建 webpack.config.js 這是我們的webpack配置文件,webpack需要通過讀取你的配置,進(jìn)行相應(yīng)的操作,類似于gulpfile.js或者gruntfile.js等。
webpack.config.js
// nodejs 中的path模塊 var path = require('path'); module.exports = { // 入口文件,path.resolve()方法,可以結(jié)合我們給定的兩個參數(shù)最后生成絕對路徑,最終指向的就是我們的index.js文件 entry: path.resolve(__dirname, '../app/index/index.js'), // 輸出配置 output: { // 輸出路徑是 myProject/output/static path: path.resolve(__dirname, '../output/static'), publicPath: 'static/', filename: '[name].[hash].js', chunkFilename: '[id].[chunkhash].js' }, module: { loaders: [ // 使用vue-loader 加載 .vue 結(jié)尾的文件 { test: /\.vue$/, loader: 'vue' } ] } }
上例中,相信你已經(jīng)看懂了我的配置,入口文件是index.js文件,配置了相應(yīng)輸出,然后使用 vue-loader 去加載 .vue 結(jié)尾的文件,接下來我們就可以構(gòu)建項(xiàng)目了,我們可以在命令行中執(zhí)行:
webpack --display-modules --display-chunks --config build/webpack.config.js
通過webpack命令,并且通過 –config 選項(xiàng)指定了我們配置文件的位置是 ‘build/webpack.config.js',并通過 –display-modules 和 –display-chunks 選項(xiàng)顯示相應(yīng)的信息。如果你執(zhí)行上面的命令,可能得到下圖的錯誤:
錯誤提示我們應(yīng)該選擇合適的loader去加載這個 ‘./app/index/index.js' 這個文件,并且說不期望index.js文件中的標(biāo)識符(Unexpected token),這是因?yàn)槲覀兪褂昧薊S6的語法 import 語句,所以我們要使用 babel-loader 去加載我們的js文件,在配置文件中添加一個loaders項(xiàng)目,如下:
// nodejs 中的path模塊 var path = require('path'); module.exports = { // 入口文件,path.resolve()方法,可以結(jié)合我們給定的兩個參數(shù)最后生成絕對路徑,最終指向的就是我們的index.js文件 entry: path.resolve(__dirname, '../app/index/index.js'), // 輸出配置 output: { // 輸出路徑是 myProject/output/static path: path.resolve(__dirname, '../output/static'), publicPath: 'static/', filename: '[name].[hash].js', chunkFilename: '[id].[chunkhash].js' }, module: { loaders: [ // 使用vue-loader 加載 .vue 結(jié)尾的文件 { test: /\.vue$/, loader: 'vue' }, { test: /\.js$/, loader: 'babel?presets=es2015', exclude: /node_modules/ } ] } }
現(xiàn)在再運(yùn)行構(gòu)建命令 : ‘webpack –display-modules –display-chunks –config build/webpack.config.js'
sorry,不出意外,你應(yīng)該得到如下錯誤:
它說沒有發(fā)現(xiàn) ‘./components/Favlist' 模塊,而我們明明有 ./components/Favlist.vue 文件,為什么它沒發(fā)現(xiàn)呢?它瞎了?其實(shí)是這樣的,當(dāng)webpack試圖去加載模塊的時候,它默認(rèn)是查找以 .js 結(jié)尾的文件的,它并不知道 .vue 結(jié)尾的文件是什么鬼玩意兒,所以我們要在配置文件中告訴webpack,遇到 .vue 結(jié)尾的也要去加載,添加 resolve 配置項(xiàng),如下:
// nodejs 中的path模塊 var path = require('path'); module.exports = { // 入口文件,path.resolve()方法,可以結(jié)合我們給定的兩個參數(shù)最后生成絕對路徑,最終指向的就是我們的index.js文件 entry: path.resolve(__dirname, '../app/index/index.js'), // 輸出配置 output: { // 輸出路徑是 myProject/output/static path: path.resolve(__dirname, '../output/static'), publicPath: 'static/', filename: '[name].[hash].js', chunkFilename: '[id].[chunkhash].js' }, resolve: { extensions: ['', '.js', '.vue'] }, module: { loaders: [ // 使用vue-loader 加載 .vue 結(jié)尾的文件 { test: /\.vue$/, loader: 'vue' }, { test: /\.js$/, loader: 'babel?presets=es2015', exclude: /node_modules/ } ] } }
這樣,當(dāng)我們?nèi)ゼ虞d ‘./components/Favlist' 這樣的模塊時,webpack首先會查找 ./components/Favlist.js 如果沒有發(fā)現(xiàn)Favlist.js文件就會繼續(xù)查找 Favlist.vue 文件,現(xiàn)在再次運(yùn)行構(gòu)建命令,我們成功了,這時我們會在我們的輸出目錄中看到一個js文件:
之所以會這樣輸出,是因?yàn)槲覀兊?webpack.config.js 文件中的輸出配置中指定了相應(yīng)的輸出信息,這個時候,我們修改 index.html ,將輸出的js文件引入:
<!DOCTYPE html> <html lang="zh"> <head> <meta name="viewport" content="width=device-width, height=device-height, initial-scale=1.0, user-scalable=no"> <meta charset="utf-8"> <title>首頁</title> </head> <body> <!-- vue的組件以自定義標(biāo)簽的形式使用 --> <favlist></favlist> <script src="../../output/static/main.ce853b65bcffc3b16328.js"></script> </body> </html>
然后用瀏覽器打開這個頁面,你可以看到你寫的代碼正確的執(zhí)行了。
那么問題來了,難道我們每次都要手動的引入輸出的js文件嗎?因?yàn)槊看螛?gòu)建輸出的js文件都帶有 hash 值,如 main.ce853b65bcffc3b16328.js,就不能更智能一點(diǎn)嗎?每次都自動寫入?怎么會不可能,否則這東西還能火嗎,要實(shí)現(xiàn)這個功能,我們就要使用webpack的插件了,html-webpack-plugin插件,這個插件可以創(chuàng)建html文件,并自動將依賴寫入html文件中。
首先安裝 html-webpack-plugin 插件:
npm install html-webpack-plugin --save-dev
然后在修改配置項(xiàng):
// nodejs 中的path模塊 var path = require('path'); var HtmlWebpackPlugin = require('html-webpack-plugin') module.exports = { // 入口文件,path.resolve()方法,可以結(jié)合我們給定的兩個參數(shù)最后生成絕對路徑,最終指向的就是我們的index.js文件 entry: path.resolve(__dirname, '../app/index/index.js'), // 輸出配置 output: { // 輸出路徑是 myProject/output/static path: path.resolve(__dirname, '../output/static'), publicPath: 'static/', filename: '[name].[hash].js', chunkFilename: '[id].[chunkhash].js' }, resolve: { extensions: ['', '.js', '.vue'] }, module: { loaders: [ // 使用vue-loader 加載 .vue 結(jié)尾的文件 { test: /\.vue$/, loader: 'vue' }, { test: /\.js$/, loader: 'babel?presets=es2015', exclude: /node_modules/ } ] }, plugins: [ new HtmlWebpackPlugin({ filename: '../index.html', template: path.resolve(__dirname, '../app/index/index.html'), inject: true }) ] }
然后再次執(zhí)行構(gòu)建命令,成功之后,看你的輸出目錄,多出來一個index.html文件,雙擊它,代碼正確執(zhí)行,你可以打開這個文件查看一下,webpack自動幫我們引入了相應(yīng)的文件。
問題繼續(xù)來了,難道每次我們都要構(gòu)建之后才能查看運(yùn)行的代碼嗎?那豈不是很沒有效率,別擔(dān)心,webpack提供了幾種方式,進(jìn)行熱加載,在開發(fā)模式中,我們使用這種方式來提高效率,這里要介紹的,是使用webpack-dev-middleware中間件和webpack-hot-middleware中間件,首先安裝兩個中間件:
npm install webpack-dev-middleware webpack-hot-middleware --save-dev
另外,還要安裝express,這是一個nodejs框架
npm install express --save-dev
在開始之前,我先簡單介紹一下這兩個中間件,之所以叫做中間件,是因?yàn)閚odejs的一個叫做express的框架中有中間件的概念,而這兩個包要作為express中間件使用,所以稱它們?yōu)橹虚g件,那么他們能干什么呢?
1、webpack-dev-middleware
我們之前所面臨的問題是,如果我們的代碼改動了,我們要想看到瀏覽器的變化,需要先對項(xiàng)目進(jìn)行構(gòu)建,然后才能查看效果,這樣對于開發(fā)效率來講,簡直就是不可忍受的一件事,試想我僅僅修改一個背景顏色就要構(gòu)建一下項(xiàng)目,這尼瑪坑爹啊,好在有webpack-dev-middleware中間件,它是對webpack一個簡單的包裝,它可以通過連接服務(wù)器服務(wù)那些從webpack發(fā)射出來的文件,它有一下幾點(diǎn)好處:
1、不會向硬盤寫文件,而是在內(nèi)存中,注意我們構(gòu)建項(xiàng)目實(shí)際就是向硬盤寫文件。
2、當(dāng)文件改變的時候,這個中間件不會再服務(wù)舊的包,你可以直接帥新瀏覽器就能看到最新的效果,這樣你就不必等待構(gòu)建的時間,所見即所得。
下面我們在build目錄中創(chuàng)建一個 dev-server.js 的文件,并寫入一下內(nèi)容:
// 引入必要的模塊 var express = require('express') var webpack = require('webpack') var config = require('./webpack.config') // 創(chuàng)建一個express實(shí)例 var app = express() // 調(diào)用webpack并把配置傳遞過去 var compiler = webpack(config) // 使用 webpack-dev-middleware 中間件 var devMiddleware = require('webpack-dev-middleware')(compiler, { publicPath: config.output.publicPath, stats: { colors: true, chunks: false } }) // 注冊中間件 app.use(devMiddleware) // 監(jiān)聽 8888端口,開啟服務(wù)器 app.listen(8888, function (err) { if (err) { console.log(err) return } console.log('Listening at http://localhost:8888') })
此時,我們在項(xiàng)目根目錄運(yùn)行下面的命令,開啟服務(wù):
node build/dev-server.js
如果看到下圖所示,證明你的服務(wù)成功開啟了:
接下來打開瀏覽器,輸入: http://localhost:8888/app/index/index.html
回車,如果不出意外,你應(yīng)該得到一個404,如下圖:
我們要對我們的 webpack.config.js 配置文件做兩處修改:
1、將 config.output.publicPath 修改為 ‘/‘:
output: { // 輸出路徑是 myProject/output/static path: path.resolve(__dirname, '../output/static'), publicPath: '/', filename: '[name].[hash].js', chunkFilename: '[id].[chunkhash].js' },
2、將 plugins 中 HtmlWebpackPlugin 中的 filename 修改為 ‘a(chǎn)pp/index/index.html'
plugins: [ new HtmlWebpackPlugin({ filename: 'app/index/index.html', template: path.resolve(__dirname, '../app/index/index.html'), inject: true }) ]
重啟服務(wù),再刷新頁面,如果看到如下界面,證明你成功了:
但是這樣開發(fā)模式下的確是成功了,可是我們直接修改了 webpack.config.js 文件,這就意味著當(dāng)我們執(zhí)行 構(gòu)建命令 的時候,配置變了,那么我們的構(gòu)建也跟著變了,所以,一個好的方式是,不去修改webpack.config.js文件,我們在build目錄下新建一個 webpack.dev.conf.js文件,意思是開發(fā)模式下要讀取的配置文件,并寫入一下內(nèi)容:
var HtmlWebpackPlugin = require('html-webpack-plugin') var path = require('path'); // 引入基本配置 var config = require('./webpack.config'); config.output.publicPath = '/'; config.plugins = [ new HtmlWebpackPlugin({ filename: 'app/index/index.html', template: path.resolve(__dirname, '../app/index/index.html'), inject: true }) ]; module.exports = config;
這樣,我們在dev環(huán)境下的配置文件中覆蓋了基本配置文件,我們只需要在dev-server.js中將
var config = require('./webpack.config')
修改為:
var config = require('./webpack.dev.conf')
即可,然后,重啟服務(wù),刷新瀏覽器,你應(yīng)該得到同樣的成功結(jié)果,而這一次當(dāng)我們執(zhí)行構(gòu)建命令:
webpack --display-modules --display-chunks --config build/webpack.config.js
并不會影響構(gòu)建輸出,因?yàn)槲覀儧]有直接修改webpack.config.js文件。
現(xiàn)在我們已經(jīng)使用 webpack-dev-middleware 搭建基本的開發(fā)環(huán)境了,但是我們并不滿足,因?yàn)槲覀兠看味家謩尤ニ⑿聻g覽器,所謂的熱加載,意思就是說能夠追蹤我們代碼的變化,并自動更新界面,甚至還能保留程序狀態(tài)。要完成熱加載,我們就需要使用另外一個中間件 webpack-hot-middleware
2、webpack-hot-middleware
webpack-hot-middleware 只配合 webpack-dev-middleware 使用,它能給你提供熱加載。
它的使用很簡單,總共分4步:
1、安裝,我們上面已經(jīng)安裝過了
2、在 webpack.dev.conf.js 配置文件中添加三個插件,如下:
var HtmlWebpackPlugin = require('html-webpack-plugin') var path = require('path'); var webpack = require('webpack'); // 引入基本配置 var config = require('./webpack.config'); config.output.publicPath = '/'; config.plugins = [ // 添加三個插件 new webpack.optimize.OccurenceOrderPlugin(), new webpack.HotModuleReplacementPlugin(), new webpack.NoErrorsPlugin(), new HtmlWebpackPlugin({ filename: 'app/index/index.html', template: path.resolve(__dirname, '../app/index/index.html'), inject: true }) ]; module.exports = config;
3、在 webpack.config.js 文件中入口配置中添加 ‘webpack-hot-middleware/client',如下:
4、在 dev-server.js 文件中使用插件
// 引入必要的模塊 var express = require('express') var webpack = require('webpack') var config = require('./webpack.dev.conf') // 創(chuàng)建一個express實(shí)例 var app = express() // 調(diào)用webpack并把配置傳遞過去 var compiler = webpack(config) // 使用 webpack-dev-middleware 中間件 var devMiddleware = require('webpack-dev-middleware')(compiler, { publicPath: config.output.publicPath, stats: { colors: true, chunks: false } }) // 使用 webpack-hot-middleware 中間件 var hotMiddleware = require('webpack-hot-middleware')(compiler) // 注冊中間件 app.use(devMiddleware) // 注冊中間件 app.use(hotMiddleware) // 監(jiān)聽 8888端口,開啟服務(wù)器 app.listen(8888, function (err) { if (err) { console.log(err) return } console.log('Listening at http://localhost:8888') })
ok,現(xiàn)在重啟的服務(wù),然后修改 Favlist.vue 中的頁面背景顏色為 ‘#000':
<style> html{ background: #000; } </style>
然后查看你的瀏覽器,是不是你還沒有刷新就已經(jīng)得帶改變了?
那么這樣就完美了嗎?還沒有,如果你細(xì)心,你會注意到,我們上面在第2步中修改了 webpack.config.js 這個基本配置文件,修改了入口配置,如下:
這也會導(dǎo)致我們之前討論過的問題,就是會影響構(gòu)建,所以我們不要直接修改 webpack.config.js 文件,我們還是在 webpack.dev.conf.js 文件中配置,如下:
var HtmlWebpackPlugin = require('html-webpack-plugin') var path = require('path'); var webpack = require('webpack'); // 引入基本配置 var config = require('./webpack.config'); config.output.publicPath = '/'; config.plugins = [ new webpack.optimize.OccurenceOrderPlugin(), new webpack.HotModuleReplacementPlugin(), new webpack.NoErrorsPlugin(), new HtmlWebpackPlugin({ filename: 'app/index/index.html', template: path.resolve(__dirname, '../app/index/index.html'), inject: true }) ]; // 動態(tài)向入口配置中注入 webpack-hot-middleware/client var devClient = 'webpack-hot-middleware/client'; Object.keys(config.entry).forEach(function (name, i) { var extras = [devClient] config.entry[name] = extras.concat(config.entry[name]) }) module.exports = config;
但是我們還是要講 webpack.config.js 文件中的入口配置修改為多入口配置的方式,這個修改不會影響構(gòu)建,所以無所謂:
entry: { index: path.resolve(__dirname, '../app/index/index.js') },
重啟你的服務(wù),刷新一下瀏覽器,然后修改 Favlist.vue 中的背景色為 green:
<style> html{ background: green; } </style>
再次查看瀏覽器,發(fā)現(xiàn)可以熱加載。但是這樣就結(jié)束了嗎?還沒有,不信你修改 index.html 文件,看看會不會熱加載,實(shí)際上不會,你還是需要手動刷新頁面,為了能夠當(dāng) index.html 文件的改動也能夠觸發(fā)自動刷新,我們還需要做一些工作。
第一步:在 dev-server.js 文件中監(jiān)聽html文件改變事件,修改后的 dev-server.js 文件如下:
// 引入必要的模塊 var express = require('express') var webpack = require('webpack') var config = require('./webpack.dev.conf') // 創(chuàng)建一個express實(shí)例 var app = express() // 調(diào)用webpack并把配置傳遞過去 var compiler = webpack(config) // 使用 webpack-dev-middleware 中間件 var devMiddleware = require('webpack-dev-middleware')(compiler, { publicPath: config.output.publicPath, stats: { colors: true, chunks: false } }) var hotMiddleware = require('webpack-hot-middleware')(compiler) // webpack插件,監(jiān)聽html文件改變事件 compiler.plugin('compilation', function (compilation) { compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) { // 發(fā)布事件 hotMiddleware.publish({ action: 'reload' }) cb() }) }) // 注冊中間件 app.use(devMiddleware) // 注冊中間件 app.use(hotMiddleware) // 監(jiān)聽 8888端口,開啟服務(wù)器 app.listen(8888, function (err) { if (err) { console.log(err) return } console.log('Listening at http://localhost:8888') })
從上面的代碼中可以看到,我們增加了如下代碼:
// webpack插件,監(jiān)聽html文件改變事件 compiler.plugin('compilation', function (compilation) { compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) { // 發(fā)布事件 hotMiddleware.publish({ action: 'reload' }) cb() }) })
這段代碼可能你看不懂,因?yàn)檫@涉及到webpack插件的編寫,讀者可以參閱下面的連接:
webpack 插件doc1
webpack 插件doc2
在這段代碼中,我們監(jiān)聽了 ‘html-webpack-plugin-after-emit' 事件,那么這個事件是從哪里發(fā)射的呢?我們通過名字可知,這個事件應(yīng)該和html-webpack-plugin這個插件有關(guān),在npm搜索html-webpack-plugin 插件,在頁面最底部我們可以發(fā)現(xiàn)如下圖:
我們可以看到,html-webpack-plugin 這個插件的確提供了幾個可選的事件,下面也提供了使用方法,這樣,我們就能夠監(jiān)聽到html文件的變化,然后我們使用下面的代碼發(fā)布一個事件:
hotMiddleware.publish({ action: 'reload' })
第二步:修改 webpack.dev.conf.js 文件如下:
var HtmlWebpackPlugin = require('html-webpack-plugin') var path = require('path'); var webpack = require('webpack'); // 引入基本配置 var config = require('./webpack.config'); config.output.publicPath = '/'; config.plugins = [ new webpack.optimize.OccurenceOrderPlugin(), new webpack.HotModuleReplacementPlugin(), new webpack.NoErrorsPlugin(), new HtmlWebpackPlugin({ filename: 'app/index/index.html', template: path.resolve(__dirname, '../app/index/index.html'), inject: true }) ]; // var devClient = 'webpack-hot-middleware/client'; var devClient = './build/dev-client'; Object.keys(config.entry).forEach(function (name, i) { var extras = [devClient] config.entry[name] = extras.concat(config.entry[name]) }) module.exports = config;
我們修改了devClient變量,將 ‘webpack-hot-middleware/client' 替換成 ‘./build/dev-client',最終會導(dǎo)致,我們?nèi)肟谂渲脮兂上旅孢@樣:
entry: { index: [ './build/dev-client', path.resolve(__dirname, '../app/index/index.js') ] },
第三步:新建 build/dev-client.js 文件,并編輯如下內(nèi)容:
var hotClient = require('webpack-hot-middleware/client') // 訂閱事件,當(dāng) event.action === 'reload' 時執(zhí)行頁面刷新 hotClient.subscribe(function (event) { if (event.action === 'reload') { window.location.reload() } })
這里我們除了引入 ‘webpack-hot-middleware/client' 之外訂閱了一個事件,當(dāng) event.action === ‘reload' 時觸發(fā),還記得我們在 dev-server.js 中發(fā)布的事件嗎:
hotMiddleware.publish({ action: 'reload' })
這樣,當(dāng)我們的html文件改變后,就可以監(jiān)聽的到,最終會執(zhí)行頁面刷新,而不需要我們手動刷新,現(xiàn)在重啟服務(wù),去嘗試能否對html文件熱加載吧。答案是yes。
好了,開發(fā)環(huán)境終于搞定了,下面我們再來談一談生產(chǎn)環(huán)境,也就是構(gòu)建輸出,我們現(xiàn)在可以執(zhí)行一下構(gòu)建命令,看看輸出的內(nèi)容是什么,為了不必每次都要輸入下面這條長命令:
我們在 package.js 文件中添加 “scripts” 項(xiàng),如下圖:
這樣,我們就可以通過執(zhí)行下面命令來進(jìn)行構(gòu)建,同時我們還增加了一條開啟開發(fā)服務(wù)器的命令:
// 構(gòu)建 npm run build // 開啟開發(fā)服務(wù)器 npm run dev
回過頭來,我們執(zhí)行構(gòu)建命令: npm run build,查看輸出內(nèi)容,如下圖:
現(xiàn)在我們只有一個js文件輸出了,并沒有css文件輸出,在生產(chǎn)環(huán)境,我們希望css文件生成單獨(dú)的文件,所以我們要使用 extract-text-webpack-plugin 插件,安裝:
npm install extract-text-webpack-plugin --save-dev
然后在build目錄下新建 webpack.prod.conf.js 文件,顧名思義,這個使我們區(qū)別于開發(fā)環(huán)境,用于生產(chǎn)環(huán)境的配置文件,并編輯一下內(nèi)容:
var HtmlWebpackPlugin = require('html-webpack-plugin') var ExtractTextPlugin = require('extract-text-webpack-plugin') var path = require('path'); var webpack = require('webpack'); // 引入基本配置 var config = require('./webpack.config'); config.vue = { loaders: { css: ExtractTextPlugin.extract("css") } }; config.plugins = [ // 提取css為單文件 new ExtractTextPlugin("../[name].[contenthash].css"), new HtmlWebpackPlugin({ filename: '../index.html', template: path.resolve(__dirname, '../app/index/index.html'), inject: true }) ]; module.exports = config;
上面的代碼中,我們覆蓋了 webpack.config.js 配置文件的 config.plugins 項(xiàng),并且添加了 config.vue 項(xiàng),補(bǔ)血藥知道為什么,就是這么用的,如果一定要知道為什么也可以,這需要你多去了解vue以及vue-loader的工作原理.
然后修改 package.json 文件中的 script 項(xiàng)為如下:
"scripts": { "build": "webpack --display-modules --display-chunks --config build/webpack.prod.conf.js", "dev": "node ./build/dev-server.js" },
我們使用 webpack.prod.conf.js 為配置去構(gòu)建,接下來執(zhí)行:
npm run build
查看你的輸出內(nèi)容,如下圖,css文件未提取出來了:
另外我們還可以添加如下插件在我們的 webpack.prod.conf.js 文件中,作為生產(chǎn)環(huán)境使用:
config.plugins = [ new webpack.DefinePlugin({ 'process.env': { NODE_ENV: '"production"' } }), // 壓縮代碼 new webpack.optimize.UglifyJsPlugin({ compress: { warnings: false } }), new webpack.optimize.OccurenceOrderPlugin(), // 提取css為單文件 new ExtractTextPlugin("../[name].[contenthash].css"), new HtmlWebpackPlugin({ filename: '../index.html', template: path.resolve(__dirname, '../app/index/index.html'), inject: true }) ];
大家可以搜索這些插件,了解他的作用,這篇文章要介紹的太多,所以我一一講解了。
到這里實(shí)際上搭建的已經(jīng)差不多了,唯一要做的就是完善,比如公共模塊的提取,如何加載圖片,對于第一個問題,如何提取公共模塊,我們可以使用 CommonsChunkPlugin 插件,在 webpack.prod.conf.js 文件中添加如下插件:
new webpack.optimize.CommonsChunkPlugin({ name: 'vendors', filename: 'vendors.js', }),
然后在 webpack.config.js 文件中配置入口文件:
entry: { index: path.resolve(__dirname, '../app/index/index.js'), vendors: [ 'Vue' ] },
上面代碼的意思是,我們把Vue.js當(dāng)做公共模塊單獨(dú)打包,你可以在這個數(shù)組中增加其他模塊,一起作為公共模塊打包成一個文件,我們執(zhí)行構(gòu)建命令,然后查看輸出,如下圖,成功提?。?/p>
對于加載圖片的問題,我們知道,webpack的哲學(xué)是一切皆是模塊,然后通過相應(yīng)的loader去加載,所以加載圖片,我們就需要使用到 url-loader,在webpack.config.js 文件中添加一個loader配置:
loaders: [ // 使用vue-loader 加載 .vue 結(jié)尾的文件 { test: /\.vue$/, loader: 'vue' }, { test: /\.js$/, loader: 'babel?presets=es2015', exclude: /node_modules/ }, // 加載圖片 { test: /\.(png|jpg|gif|svg)$/, loader: 'url', query: { limit: 10000, name: '[name].[ext]?[hash:7]' } } ]
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持創(chuàng)新互聯(lián)。
分享題目:詳解基于webpack和vue.js搭建開發(fā)環(huán)境
當(dāng)前網(wǎng)址:http://jinyejixie.com/article30/ggejso.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站內(nèi)鏈、網(wǎng)站策劃、企業(yè)網(wǎng)站制作、面包屑導(dǎo)航、網(wǎng)站制作、定制網(wǎng)站
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)