Vue + webpack 项目实践
最近在內(nèi)部項(xiàng)目中做了一些基于 vue + webpack 的嘗試,在小范圍和同事們探討之后,還是蠻多同學(xué)認(rèn)可和喜歡的,所以通過 blog 分享給更多人。
首先,我會(huì)先簡(jiǎn)單介紹一下 vue 和 webpack:
(當(dāng)然如果你已經(jīng)比較熟悉它們的話前兩個(gè)部分可以直接跳過)
介紹 vue
Vue.js?是一款極簡(jiǎn)的 mvvm 框架,如果讓我用一個(gè)詞來形容它,就是?“輕·巧”?。如果用一句話來描述它,它能夠集眾多優(yōu)秀逐流的前端框架之大成,但同時(shí)保持簡(jiǎn)單易用。廢話不多說,來看幾個(gè)例子:
<script src="vue.js"></script><div id="demo">{{message}}<input v-model="message"> </div><script>var vm = new Vue({el: '#demo',data: {message: 'Hello Vue.js!'}}) </script>首先,代碼分兩部分,一部分是 html,同時(shí)也是視圖模板,里面包含一個(gè)值為?message的文本何一個(gè)相同值的輸入框;另一部分是 script,它創(chuàng)建了一個(gè) vm 對(duì)象,其中綁定的 dom 結(jié)點(diǎn)是?#demo,綁定的數(shù)據(jù)是?{message: 'Hello Vue.js'},最終頁(yè)面的顯示效果就是一段?Hello Vue.js?文本加一個(gè)含相同文字的輸入框,更關(guān)鍵的是,由于數(shù)據(jù)是雙向綁定的,所以我們修改文本框內(nèi)文本的同時(shí),第一段文本和被綁定的數(shù)據(jù)的?message?字段的值都會(huì)同步更新——而這底層的復(fù)雜邏輯,Vue.js 已經(jīng)全部幫你做好了。
再多介紹一點(diǎn)
我們還可以加入更多的 directive,比如:
<script src="vue.js"></script><div id="demo2"><img title="{{name}}" alt="{{name}}" v-attr="src: url"><input v-model="name"><input v-model="url"> </div><script>var vm = new Vue({el: '#demo2',data: {name: 'taobao',url: 'https://www.taobao.com/favicon.ico'}}) </script>這里的視圖模板加入了一個(gè)?<img>?標(biāo)簽,同時(shí)我們看到了 2 個(gè)特性的值都寫作了?{{name}}。這樣的話,圖片的?title?和?alt?特性值就都會(huì)被綁定為字符串?'taobao'。
如果想綁定的特性是像?img[src]?這樣的不能在 html 中隨意初始化的 (可能默認(rèn)會(huì)產(chǎn)生預(yù)期外的網(wǎng)絡(luò)請(qǐng)求),沒關(guān)系,有?v-attr="src: url"?這樣的寫法,把被綁定的數(shù)據(jù)里的?url?同步過來。
沒有介紹到的功能還有很多,推薦大家來我(發(fā)起并)翻譯的Vue.js 中文文檔
web 組件化
最后要介紹 Vue.js 對(duì)于 web 組件化開發(fā)的思考和設(shè)計(jì)
如果我們要開發(fā)更大型的網(wǎng)頁(yè)或 web 應(yīng)用,web 組件化的思維是非常重要的,這也是今天整個(gè)前端社區(qū)長(zhǎng)久不衰的話題。
Vue.js 設(shè)計(jì)了一個(gè)?*.vue?格式的文件,令每一個(gè)組件的樣式、模板和腳本集合成了一整個(gè)文件,?每個(gè)文件就是一個(gè)組件,同時(shí)還包含了組件之間的依賴關(guān)系,麻雀雖小五臟俱全,整個(gè)組件從外觀到結(jié)構(gòu)到特性再到依賴關(guān)系都一覽無余?:
并且支持預(yù)編譯各種方言:
這樣再大的系統(tǒng)、在復(fù)雜的界面,也可以用這樣的方式庖丁解牛。當(dāng)然這種組件的寫法是需要編譯工具才能最終在瀏覽器端工作的,下面會(huì)提到一個(gè)基于 webpack 的具體方案。
小結(jié)
從功能角度,template, directive, data-binding, components 各種實(shí)用功能都齊全,而 filter, computed var, var watcher, custom event 這樣的高級(jí)功能也都洋溢著作者的巧思;從開發(fā)體驗(yàn)角度,這些設(shè)計(jì)幾乎是完全自然的,沒有刻意設(shè)計(jì)過或欠考慮的感覺,只有個(gè)別不得已的地方帶了自己框架專屬的?v-?前綴。從性能、體積角度評(píng)估,Vue.js 也非常有競(jìng)爭(zhēng)力!
介紹 webpack
webpack?是另一個(gè)近期發(fā)現(xiàn)的好東西。它主要的用途是通過 CommonJS 的語(yǔ)法把所有瀏覽器端需要發(fā)布的靜態(tài)資源做相應(yīng)的準(zhǔn)備,比如資源的合并和打包。
舉個(gè)例子,現(xiàn)在有個(gè)腳本主文件?app.js?依賴了另一個(gè)腳本?module.js
// app.js var module = require('./module.js') ... module.x ...// module.js exports.x = ...則通過?webpack app.js bundle.js?命令,可以把?app.js?和?module.js?打包在一起并保存到?bundle.js
同時(shí) webpack 提供了強(qiáng)大的 loader 機(jī)制和 plugin 機(jī)制,loader 機(jī)制支持載入各種各樣的靜態(tài)資源,不只是 js 腳本、連 html, css, images 等各種資源都有相應(yīng)的 loader 來做依賴管理和打包;而 plugin 則可以對(duì)整個(gè) webpack 的流程進(jìn)行一定的控制。
比如在安裝并配置了 css-loader 和 style-loader 之后,就可以通過?require('./bootstrap.css')?這樣的方式給網(wǎng)頁(yè)載入一份樣式表。非常方便。
webpack 背后的原理其實(shí)就是把所有的非 js 資源都轉(zhuǎn)換成 js (如把一個(gè) css 文件轉(zhuǎn)換成“創(chuàng)建一個(gè)?style?標(biāo)簽并把它插入?document”的腳本、把圖片轉(zhuǎn)換成一個(gè)圖片地址的 js 變量或 base64 編碼等),然后用 CommonJS 的機(jī)制管理起來。一開始對(duì)于這種技術(shù)形態(tài)我個(gè)人還是不太喜歡的,不過隨著不斷的實(shí)踐和體驗(yàn),也逐漸習(xí)慣并認(rèn)同了。
最后,對(duì)于之前提到的 Vue.js,作者也提供了一個(gè)叫做 vue-loader 的?npm 包,可以把?*.vue?文件轉(zhuǎn)換成 webpack 包,和整個(gè)打包過程融合起來。所以有了 Vue.js、webpack 和 vue-loader,我們自然就可以把它們組合在一起試試看!
項(xiàng)目實(shí)踐流程
回到正題。今天要分享的是,是基于上面兩個(gè)東西:Vue.js 和 webpack,以及把它們串聯(lián)起來的 vue-loader
Vue.js 的作者以及提供了一個(gè)基于它們?nèi)叩捻?xiàng)目示例?(鏈接已失效)。而我們的例子會(huì)更貼近實(shí)際工作的場(chǎng)景,同時(shí)和團(tuán)隊(duì)之前總結(jié)出來的項(xiàng)目特點(diǎn)和項(xiàng)目流程相吻合。
目錄結(jié)構(gòu)設(shè)計(jì)
- <components>?組件目錄,一個(gè)組件一個(gè)?.vue?文件
- a.vue
- b.vue
- <lib>?如果實(shí)在有不能算組件,但也不來自外部 (tnpm) 的代碼,可以放在這里
- foo.css
- bar.js
- <src>?主應(yīng)用/頁(yè)面相關(guān)文件
- app.html?主 html
- app.vue?主 vue
- app.js?通常做的事情只是?var Vue = require('vue'); new Vue(require('./app.vue'))
- <dist>?(ignored)
- <node_modules>?(ignored)
- gulpfile.js?設(shè)計(jì)項(xiàng)目打包/監(jiān)聽等任務(wù)
- package.json?記錄項(xiàng)目基本信息,包括模塊依賴關(guān)系
- README.md?項(xiàng)目基本介紹
打包
通過?gulpfile.js?我們可以設(shè)計(jì)整套基于 webpack 的打包/監(jiān)聽/調(diào)試的任務(wù)
在?gulp-webpack?包的官方文檔里推薦的寫法是這樣的:
var gulp = require('gulp'); var webpack = require('gulp-webpack'); var named = require('vinyl-named'); gulp.task('default', function() {return gulp.src(['src/app.js', 'test/test.js']).pipe(named()).pipe(webpack()).pipe(gulp.dest('dist/')); });我們對(duì)這個(gè)文件稍加修改,首先加入 vue-loader
tnpm install vue-loader --save.pipe(webpack({module: {loaders: [{ test: /\.vue$/, loader: 'vue'}]} }))其次,把要打包的文件列表從?gulp.src(...)?中抽出來,方便將來維護(hù),也有機(jī)會(huì)把這個(gè)信息共享到別的任務(wù)
var appList = ['main', 'sub1', 'sub2']gulp.task('default', function() {return gulp.src(mapFiles(appList, 'js'))... })/*** @private*/ function mapFiles(list, extname) {return list.map(function (app) {return 'src/' + app + '.' + extname}) }現(xiàn)在運(yùn)行?gulp?命令,相應(yīng)的文件應(yīng)該就打包好并生成在了?dist?目錄下。然后我們?cè)?src/*.html?中加入對(duì)這些生成好的?js?文件的引入:
<!DOCTYPE html> <html> <head><meta charset="utf-8"><title>Main</title> </head> <body><div id="app"></div><script src="../dist/main.js"></script> </body> </html>用瀏覽器打開?src/main.html?這時(shí)頁(yè)面已經(jīng)可以正常工作了
加入監(jiān)聽
監(jiān)聽更加簡(jiǎn)單,只要在剛才?webpack(opt)?的參數(shù)中加入?watch: true?就可以了。
.pipe(webpack({module: {loaders: [{ test: /\.vue$/, loader: 'vue'}]},watch: true }))當(dāng)然最好把打包和監(jiān)聽設(shè)計(jì)成兩個(gè)任務(wù),分別起名為?bundle?和?watch:
gulp.task('bundle', function() {return gulp.src(mapFiles(appList, 'js')).pipe(named()).pipe(webpack(getConfig())).pipe(gulp.dest('dist/')) })gulp.task('watch', function() {return gulp.src(mapFiles(appList, 'js')).pipe(named()).pipe(webpack(getConfig({watch: true}))).pipe(gulp.dest('dist/')) })/*** @private*/ function getConfig(opt) {var config = {module: {loaders: [{ test: /\.vue$/, loader: 'vue'}]}}if (!opt) {return config}for (var i in opt) {config[i] = opt}return config }現(xiàn)在你可以不必每次修改文件之后都運(yùn)行?gulp bundle?才能看到最新的效果,每次改動(dòng)之后直接刷新瀏覽器即可。
調(diào)試
打包好的代碼已經(jīng)不那么易讀了,直接在這樣的代碼上調(diào)試還是不那么方便的。這個(gè)時(shí)候,webpack + vue 有另外一個(gè)現(xiàn)成的東西:source map 支持。為 webpack 加入這個(gè)配置字段?devtool: 'source-map':
var config = { module: { loaders: [ { test: /.vue$/, loader: 'vue'} ] }, devtool: 'source-map' }
再次運(yùn)行?gulp bundle?或?gulp watch?試試看,是不是開發(fā)者工具里 debug 的時(shí)候,可以追蹤斷點(diǎn)到源代碼了呢:)
完整的 javascript 代碼如下:
var gulp = require('gulp') var webpack = require('gulp-webpack') var named = require('vinyl-named')var appList = ['main']gulp.task('default', ['bundle'], function() {console.log('done') })gulp.task('bundle', function() {return gulp.src(mapFiles(appList, 'js')).pipe(named()).pipe(webpack(getConfig())).pipe(gulp.dest('dist/')) })gulp.task('watch', function() {return gulp.src(mapFiles(appList, 'js')).pipe(named()).pipe(webpack(getConfig({watch: true}))).pipe(gulp.dest('dist/')) })/*** @private*/ function getConfig(opt) {var config = {module: {loaders: [{ test: /\.vue$/, loader: 'vue'}]},devtool: 'source-map'}if (!opt) {return config}for (var i in opt) {config[i] = opt[i]}return config }/*** @private*/ function mapFiles(list, extname) {return list.map(function (app) {return 'src/' + app + '.' + extname}) }最后,杜拉拉不如紫羅蘭
做出一個(gè) vue + webpack 的 generator,把這樣的項(xiàng)目體驗(yàn)分享給更多的人。目前我基于團(tuán)隊(duì)內(nèi)部在使用的輕量級(jí)腳手架工具寫了一份名叫?just-vue?的 generator,目前這個(gè) generator 還在小范圍試用當(dāng)中,待比較成熟之后,再分享出來
總結(jié)
其實(shí)上面提到的?just-vue?腳手架已經(jīng)遠(yuǎn)不止文章中介紹的東西了,?我們?cè)跇I(yè)務(wù)落地的“最后一公里”做了更多的沉淀和積累,比如自動(dòng)圖片上傳與畫質(zhì)處理、rem單位自動(dòng)換算、服務(wù)端/客戶端/數(shù)據(jù)埋點(diǎn)接口的梳理與整合、自動(dòng)化 htmlone 打包與 awp 發(fā)布等等。它們?yōu)橹С謽I(yè)務(wù)的開發(fā)者提供了更簡(jiǎn)單高效的工作體驗(yàn)。?篇幅有限,更多內(nèi)容我也希望將來有機(jī)會(huì)再多分享出來。
最后再次希望大家如果有興趣的話可以來玩一下,無線前端組內(nèi)的同學(xué)我都愿意提供一對(duì)一入門指導(dǎo):)
Just Vue!
?
from:?http://jiongks.name/blog/just-vue/
總結(jié)
以上是生活随笔為你收集整理的Vue + webpack 项目实践的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Vue.js 渲染函数 JSX
- 下一篇: 从浏览器多进程到JS单线程,JS运行机制