本文適用有前端基礎(chǔ)但沒有Vue基礎(chǔ)的讀者。
作為一個正正經(jīng)經(jīng)的后端開發(fā),雖然早年自稱過全棧野哭,但是隨著前端技術(shù)發(fā)展的越來越快,技術(shù)棧越來越多幻件,現(xiàn)在已經(jīng)不敢自稱全棧拨黔。
早年搭建管理后臺都是采用bootstrap,由后端渲染頁面绰沥。但是有很多東西是后端渲染無法做到的篱蝇。偶然發(fā)現(xiàn)一個組件庫——iView贺待,這是一個高質(zhì)量的vue組件庫,涵蓋了你可以想象到的任何組件零截。但是因?yàn)橹皼]有vue基礎(chǔ)麸塞,所以踩了很多坑,這里總結(jié)一下涧衙。
0x00如何跳轉(zhuǎn)頁面
由這個問題就引出了vue-router哪工。它是一個前端路由,也就是說弧哎,通過它進(jìn)行的跳轉(zhuǎn)操作是并沒有經(jīng)過后端的雁比。
<router-link to="/foo">Go to Foo</router-link>
this.$router.push({ path: '/user', params: { userId: 123 }})
上面是兩種頁面跳轉(zhuǎn)方式,其中push的方式最常見撤嫩,這里為了方便理解使用了path偎捎,在實(shí)際應(yīng)用中路由表會給每個路由分配一個name,在push的時候就可以直接push name進(jìn)去了序攘。
參考文檔:https://router.vuejs.org/
0x01如何獲取參數(shù)
上面說了跳轉(zhuǎn)茴她,如果跳轉(zhuǎn)的時候帶了參數(shù),那么如何獲取到傳遞過來的參數(shù)呢两踏?
this.$route.params['id']
this.$route.query.id
0x02如何變更數(shù)據(jù)
這里就要了解雙向綁定這個東西了败京。何為雙向綁定?
其實(shí)簡單來說就是在vue中也會有model和view的概念梦染。雙向綁定就是針對他們來說,當(dāng)我們用JavaScript代碼更新Model時朴皆,View就會自動更新帕识。當(dāng)用戶變更了View,Model的數(shù)據(jù)也自動被更新了遂铡,這種情況就是雙向綁定肮疗。
舉個例子就是,當(dāng)用戶填寫表單數(shù)據(jù)的時候扒接,表單數(shù)據(jù)變更了伪货,這個時候model里的數(shù)據(jù)也同步變更了,這種方式就避免了早期原生js代碼中那種還得通過dom結(jié)構(gòu)自行去獲取form表單的數(shù)據(jù)再拼接數(shù)據(jù)钾怔。
同樣碱呼,在表格中,如果你要刪除一行表格需要如何操作呢宗侦?在沒有雙向綁定之前你需要分別去刪除dom結(jié)構(gòu)中的dom和數(shù)據(jù)中相應(yīng)的行數(shù)據(jù)愚臀。而現(xiàn)在,你只需要刪除掉數(shù)據(jù)中的行數(shù)據(jù)矾利,dom結(jié)構(gòu)壓根不需要你關(guān)心姑裂。當(dāng)然這個只是例子馋袜,實(shí)際應(yīng)用中一般都是重新調(diào)用接口刷新全部數(shù)據(jù),表格dom也會全部重新刷新舶斧。
0x03如何在表格中渲染自定義內(nèi)容
在一個表格中我們經(jīng)常需要渲染一些復(fù)雜的內(nèi)容欣鳖,這里就需要用到reader函數(shù)。
{
columns: [
{
title: 'Type',
key: 'type',
width: 160
},
{
title: '操作',
key: 'action',
width: 100,
render: (h, params) => {
return h('div', [
h('Button', {
props: {
type: 'primary',
size: 'small'
},
style: {
marginRight: '5px'
},
on: {
click: () => {
this.show(params.index)
}
}
}, '查看')
]);
}
}
]
}
上面的代碼是一個表格的columns茴厉,這里可以看到在操作這一列中我們渲染了一個按鈕泽台,重點(diǎn)在h函數(shù),他的第一個參數(shù)是標(biāo)簽名呀忧,最后一個參數(shù)是標(biāo)簽中間的Text师痕,中間的參數(shù)是標(biāo)簽的屬性和事件之類。需要注意的是而账,中間的參數(shù)是可以省略的胰坟,也就是只有兩個參數(shù)∨⒎基本屬性可以寫在props里笔横;css可以寫在style里,注意采用駝峰寫法咐吼;事件寫在on里吹缔。
還有需要注意的是:h函數(shù)是可以互相嵌套的,就是通過這種嵌套組合不同的dom結(jié)構(gòu)锯茄。當(dāng)然這種方式其實(shí)并不友好厢塘,還有更方便的方式是jsx,通過jsx你可以直接在render函數(shù)里返回一個dom肌幽,這里就不展開講了晚碾。
0x04如何格式化日期
實(shí)際開發(fā)中后端接口有時候會返回UTC時間,那么如何優(yōu)雅的對其進(jìn)行格式化呢喂急。答案是moment格嘁。npm install moment
然后在main.js里添加如下代碼即可
Vue.prototype.$moment = moment;
Vue.filter('moment', function(value, formatString) {
formatString = formatString || 'YYYY-MM-DD HH:mm:ss';
return moment(value).format(formatString)
})
if (!String.prototype.moment) {
String.prototype.moment = function(formatString) {
formatString = formatString || 'YYYY-MM-DD HH:mm:ss';
return moment(this).format(formatString)
};
}
在使用的時候,如果是在dom結(jié)構(gòu)中廊移,用法是{{ data.time | moment }}
糕簿。
如果是在js中,用法是params.row.create_time.moment()
0x05如何提交數(shù)據(jù)
原先vue官方推薦的網(wǎng)絡(luò)庫是vue-resource狡孔,現(xiàn)在已經(jīng)改成axios了懂诗。一樣npm install axios
,然后再main.js中加入如下代碼
Vue.prototype.$http = axios;
這樣步氏,你就可以在任意vue文件中通過下面這種方式使用了
this.$http.get('/api/stacks/').then((response) => {
console.log(response.data)
})
this.$http.post('/api/stacks/').then((response) => {
console.log(response.data)
}).catch((e) => {
console.log(e)
})
當(dāng)然响禽,這真是基本用法,在復(fù)雜應(yīng)用中,為了方便后期維護(hù)最好還是封裝一下芋类。
參考文檔:https://github.com/axios/axios
0x06箭頭函數(shù)
通過上面的幾段代碼不知道你有沒有發(fā)現(xiàn)一個東西
render: (h, params) => {
},
click: () => {
this.show(params.index)
}
這個其實(shí)也是一種函數(shù)的形式隆嗅,一般情況下你可能更習(xí)慣用傳統(tǒng)的function,但是你沒有想過為什么很多示例中都用箭頭函數(shù)侯繁?
<script>
export default {
data () {
return {
stacks: []
}
},
methods: {
envList: function (groupID) {
this.$http.get('/api/stacks/').then((response) => {
this.stacks = response.data
console.log(this.stacks)
})
},
}
}
</script>
就像上面的代碼胖喳,如果不用箭頭函數(shù)會怎樣?
<script>
export default {
data () {
return {
stacks: []
}
},
methods: {
envList: function (groupID) {
this.$http.get('/api/stacks/').then(function(response) {
this.stacks = response.data
console.log(this.stacks)
})
},
}
}
</script>
如果你運(yùn)行上面這段代碼你就會發(fā)現(xiàn)報錯了贮竟。在傳統(tǒng)的function中this是訪問不到data中return的數(shù)據(jù)的丽焊。
0x07如何組件化
組件化有兩個好處,一是代碼復(fù)用咕别,二是解耦技健。
最常見的需求是把模態(tài)框組件化,這樣就可以避免模態(tài)框的代碼耦合在頁面的代碼中惰拱。
<script>
export default {
props: {
title: ''
},
data() {
return {
show: false,
loading: false,
}
},
methods: {
open: function (id) {
this.show = true;
if (id !== undefined) {
group.getById(id, (body) => {
this.formValidate = body.data;
})
}
},
submit: function (name) {
},
}
}
</script>
組件的代碼其實(shí)跟普通頁面的代碼差別不大雌贱。就是多了一個props,里面包含了這個組件的屬性偿短。比如這里定義了一個title欣孤,那么在使用的時候你就可以傳一個title進(jìn)來。
<ModalCreation ref="modalCreation" @on-success="refreshList" title="創(chuàng)建服務(wù)"></ModalCreation>
可以看到昔逗,這個組件還提供了一個on-success的事件降传,那么這是怎么做到的呢?
其實(shí)也很簡單勾怒,只需要在組件內(nèi)執(zhí)行this.$emit('on-success', body.data.id);
即可發(fā)送一個事件出來婆排。
那么如何優(yōu)雅的調(diào)起組件呢?
$refs.modalCreation.open()
0x08Vue到底是什么
官方的介紹是:它是一套用于構(gòu)建用戶界面的漸進(jìn)式框架笔链。 但是對于初學(xué)者來說還是云里霧里泽论。這里的重點(diǎn)其實(shí)是構(gòu)建,簡單來說就是你按照vue的方式寫html卡乾、css和js,然后它會幫你編譯成瀏覽器可以運(yùn)行的js文件缚够。從本質(zhì)上說幔妨,前端技術(shù)還是html、css和js這三大件谍椅,早期前端開發(fā)是直接寫原生js代碼误堡,現(xiàn)在是用vue或者react的方式。其實(shí)跟后端常見的MVC結(jié)構(gòu)類似雏吭,早期PHP也是直接寫原生PHP锁施,每個php文件里各種include、require,后來才有的各種框架悉抵,像Yaf肩狂、TP等。
0x09如何部署
這個問題的答案其實(shí)有很多種姥饰。它是由你的系統(tǒng)架構(gòu)決定的傻谁。但是在將如何部署之前需要先講一個概念——SPA(單頁面應(yīng)用)。
SPA即一個html列粪,全站只有一個html审磁,內(nèi)容如下
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<title>Chons</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
<link rel="stylesheet" href="/dist/main.css">
</head>
<body>
<div id="app"></div>
<script type="text/javascript" src="/dist/vendors.js"></script>
<script type="text/javascript" src="/dist/main.js"></script>
</body>
</html>
Vue編譯出來的文件其實(shí)初始的html就只有上面這一點(diǎn)。也就是說岂座,服務(wù)端只需要渲染這一個html文件态蒂,其他所有的事兒都交給js去處理了。
說回如何部署费什,實(shí)際上要做的事就是在服務(wù)端輸出這個html文件和css钾恢、js。
一般來說吕喘,在前后端分離的架構(gòu)中赘那,這部分代碼是屬于前端的工作,那么前端工程師們通常會用node來渲染html氯质。
而如果你是前后端自己一個人開發(fā)募舟,你可以直接使用后端服務(wù)來輸出這個html。
這里有一個坑是前后端路由問題闻察。前端路由實(shí)際上有三種模式拱礁,具體解釋如下:
### mode
* 類型: `string`
* 默認(rèn)值: `"hash" (瀏覽器環(huán)境) | "abstract" (Node.js 環(huán)境)`
* 可選值: `"hash" | "history" | "abstract"`
配置路由模式:
* `hash`: 使用 URL hash 值來作路由。支持所有瀏覽器辕漂,包括不支持 HTML5 History Api 的瀏覽器呢灶。
* `history`: 依賴 HTML5 History API 和服務(wù)器配置。查看 [HTML5 History 模式](https://router.vuejs.org/zh-cn/essentials/history-mode.html)钉嘹。
* `abstract`: 支持所有 JavaScript 運(yùn)行環(huán)境鸯乃,如 Node.js 服務(wù)器端。**如果發(fā)現(xiàn)沒有瀏覽器的 API跋涣,路由會自動強(qiáng)制進(jìn)入這個模式缨睡。**
hash模式實(shí)際上是采用錨的方式,也就是你見過的一些后臺網(wǎng)站的網(wǎng)址是http://www.ybc.com/#/user/home
這種模式的路由你可以只在服務(wù)端配置根路徑/
的路由輸出html就可以了陈辱,因?yàn)椴徽?號后面輸入什么都會打到后端路由的根路徑下奖年。
但是,如果你想去掉#號呢沛贪,那就需要使用history模式陋守,這種模式就要求服務(wù)端所有的路由全部輸出那個html震贵。
0x0A開發(fā)環(huán)境下如何訪問靜態(tài)文件
如果你是采用后端渲染的方式,那么為了統(tǒng)一流程水评,在開發(fā)環(huán)境下也是需要由后端代理前端路由猩系。那么問題就來了,線上環(huán)境時npm run build
打包出來的靜態(tài)文件之碗,直接掛載靜態(tài)文件所在目錄就行了蝙眶。那在開發(fā)環(huán)境還沒有打包,如何訪問到正在開發(fā)的靜態(tài)文件呢褪那?這里就有一個webpack-dev-server的坑幽纷。
在開發(fā)環(huán)境下當(dāng)你執(zhí)行npm run dev
的時候?qū)嶋H上是啟動了webpack提供的一個簡易web-server,即webpack-dev-server博敬。它的作用是就是把編譯好的靜態(tài)文件掛載起來讓你能夠訪問友浸。所以你會發(fā)現(xiàn)你無論如何也找不到開發(fā)過程中生成的靜態(tài)文件,因?yàn)樗揪蜎]有輸出出來偏窝,而是在內(nèi)存中收恢。
好在webpack-dev-server還是提供了一個方式可以讓我們看到這些文件 http://localhost:8080/webpack-dev-server
如下圖:
找到這些靜態(tài)文件就好辦了,既然沒有生成文件祭往,那我們只能在后端服務(wù)中代理這些靜態(tài)文件
func main() {
app := neo.App()
app.Conf.Parse("conf/local.toml")
api.Register(app)
indexTpl := "index.html"
if conf.Tree.GetEnv() == ENV {
app.Get("/static/*", func(ctx *neo.Ctx) (int, error) {
resp, err := http.Get("http://localhost:8080" + ctx.Req.URL.String())
if err != nil {
log.Println(err.Error())
}
respBody, err := ioutil.ReadAll(resp.Body)
ctx.Res.Header().Set("Content-Type", resp.Header.Get("Content-Type"))
return resp.StatusCode, ctx.Res.Raw(respBody)
})
} else {
indexTpl = "index_prod.html"
app.Serve("/static", "./assets/dist/static")
}
app.Templates("./assets/dist/*")
app.Region().Get("*", func(ctx *neo.Ctx) (int, error) {
return 200, ctx.Res.Tpl(indexTpl, nil)
})
app.Start()
}
0x0B結(jié)語
前端的技術(shù)棧實(shí)在是太零散了伦意,幾乎每走一步都是坑。越來越佩服前端大牛們了~