項目腳手架
在這里我使用vue-cli來進行安裝
# 全局安裝 vue-cli
$ npm install --global vue-cli
# 創建一個基于 webpack 模板的新項目
$ vue init webpack VueDemo
# 安裝依賴,運行
$ cd VueDemo
$ npm install
$ npm run dev
查看項目結構,index.html/main.js是項目入口,App.vue是項目的根組件,通過router/index.js路由控制來進行路由切換。
vue實例
每個 Vue 應用都是通過 Vue 函數創建一個新的 Vue 實例開始的。詳細可以查看API。Vue Api
new Vue({
el: '#app',
router,
template: '<App/>',
components: { App }
})
Vue實例的生命周期如下:
vue 組件
vue 組件有兩種書寫方法。一種是使用vue實例來注冊,另一種是單文件組件。我們項目里面使用的是單文件組件的方式,以.vue后綴的文件就是vue組件。
- 通過vue實例來注冊
<div id="app">
<runoob></runoob>
</div>
<script>
// 注冊
Vue.component('runoob', {
template: '<h1>自定義組件!</h1>'
})
// 創建根實例
new Vue({
el: '#app'
})
</script>
- 單文件組件。
文件包含template、script、style三部分。template是需要渲染到HTML里面的模版。script為組件的腳本,在此處定義組件。style為css樣式。
當一個組件被定義,data 必須聲明為返回一個初始數據對象的函數,因為組件可能被用來創建多個實例。如果 data 仍然是一個純粹的對象,則所有的實例將共享引用同一個數據對象!通過提供 data 函數,每次創建一個新實例后,我們能夠調用 data 函數,從而返回初始數據的一個全新副本數據對象
注意,不應該對 data 屬性使用箭頭函數 (例如data: () => { return { a: this.myProp }})。理由是箭頭函數綁定了父級作用域的上下文,所以 this 將不會按照期望指向 Vue 實例,this.myProp 將是 undefined。
<template>
<div class="hello">
<h1>{{ msg }}</h1>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
data () {
return {
msg: '插值測試'
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h1, h2 {
font-weight: normal;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>
組件的使用和通訊
在vue文件中使用其他組件的方式如下,組件的標簽名稱必須跟引入時的名稱一致,不區分大小寫。
<template>
<Test msg="asdf"></Test>
</template>
<script>
import Test from '@/components/Test'
export default {
name: 'HelloWorld',
components: {
Test
}
}
</script>
父組件向子組件傳遞值時可以使用Prop 傳遞數據。例如上面例子里面的往Test組件傳遞msg的值,子組件實現如下:
<template>
<span>測試頁面:{{ msg }}</span>
</template>
<script>
export default {
name: 'Test',
props: ['msg']
}
</script>
如果需要綁定動態的值則使用v-bind來實現
<Test :msg="msg"></Test>
子組件給父組件傳遞數據。使用emit來傳遞。v-on可以縮寫成@
<button v-on:click="chirdSay()">子組件按鈕</button>
methods: {
chirdSay: function () {
this.$emit('chirdSay', this.msg)
}
}
<Test :msg="array[0]" v-on:chirdSay="chirdSay"></Test>
chirdSay: function (data) {
alert(data.name)
}
組件插槽
使用slot可以將父組件的內容插入子組件。
<!-- 子組件 -->
<slot name="testSlot"></slot>
<!-- 父組件 -->
<Test :msg="array[0]" v-on:chirdSay="chirdSay">
<p slot="testSlot">testSlot</p>
</Test>
數據綁定
vue可以使用{{data}}的方式來進行數據渲染,這種用法其實是v-text指令簡寫
<h2 v-text="msg"></h2>
。類似的有v-html,綁定html格式文件。v-bind:href,綁定標簽屬性,<input type="text" v-bind:value="msg"/>
,v-bind:value可以縮寫成:value。
如果需要進行雙向數據綁定的時候,這個時候我們需要使用的是v-model。
<input type="text" v-model="msg"/>
事件綁定
vue使用v-on指令來進行事件綁定,在這里我們測試一下click事件,v-on:click可以縮寫成@click。代碼如下
<button v-on:click="say('hi')">Say Hi</button>
......
methods: {
say: function (data) {
alert(data)
}
}
循環、判斷
判斷是否顯示使用v-if/v-show,v-if是通過刪除增加dom元素,v-show是改變css的display屬性來進行顯示隱藏。v-if還可以跟v-else一塊使用。
<p v-if="show">顯示</p>
<p v-else>不顯示</p>
循環使用v-for指令來實現。
<ui v-for="item in array" v-bind:key="item.id">
<li>{{item.name}}</li>
</ui>
遍歷對象屬性,v-for可以通過下面的方式遍歷對象。同時v-for也提供了(data,key,index)的方式來遍歷,第一個參數為屬性值,第二個為屬性名稱,第三個參數為索引。
<ui v-for="item in array" :key="item.id">
<li v-for="(o, key, index) in item">{{key}}:{{o}}:{{index}}</li>
</ui>
跟v-if配合使用
<li v-for="todo in todos" v-if="!todo.isComplete">
{{ todo }}
</li>
Class/style綁定
使用v-bind:class/style進行樣式綁定。
<p v-bind:class="{active: active}" v-on:click="activeRed()">綁定class測試,點擊激活紅色</p>
activeRed: function () {
this.active = true
}
.active {
color: red;
}
多個判斷條件
<div class="static"
v-bind:class="{ active: isActive, 'text-danger': hasError }">
</div>
也可以直接綁定數值對象,下面例子跟上面等價
<div v-bind:class="classObject"></div>
data: {
classObject: {
active: true,
'text-danger': false
}
}
其他用法
<div v-bind:class="[isActive ? activeClass : '', errorClass]"></div>
<div v-bind:class="[{ active: isActive }, errorClass]"></div>
<div :class="[{active: item.classifyId == activeId}]"></div>
綁定內聯樣式
<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
<div v-bind:style="styleObject"></div>
data: {
styleObject: {
color: 'red',
fontSize: '13px'
}
}
數組更新檢測
Vue 包含一組觀察數組的變異方法,所以它們也將會觸發視圖更新。這些方法如下:
- push()
- pop()
- shift()
- unshift()
- splice()
- sort()
- reverse()
注意:由于 JavaScript 的限制,Vue 不能檢測以下變動的數組:
- 當你利用索引直接設置一個項時,例如:vm.items[indexOfItem] = newValue
解決方法:
// Vue.set
Vue.set(example1.items, indexOfItem, newValue)
// Array.prototype.splice
example1.items.splice(indexOfItem, 1, newValue)
- 當你修改數組的長度時,例如:vm.items.length = newLength
解決方法:
example1.items.splice(newLength)
對象更改檢測
還是由于 JavaScript 的限制,Vue 不能檢測對象屬性的添加或刪除,例如:
var vm = new Vue({
data: {
a: 1
}
})
// `vm.a` 現在是響應式的
vm.b = 2
// `vm.b` 不是響應式的
解決方法:
你可以添加一個新的 age 屬性到嵌套的 userProfile 對象:
Vue.set(vm.userProfile, 'age', 27)
你還可以使用 vm.$set 實例方法,它只是全局 Vue.set 的別名:
this.$set(this.userProfile, 'age', 27)
動態多選框
<select v-model="selected">
<option v-for="option in options" v-bind:value="option.value">
{{ option.text }}
</option>
</select>
<span>Selected: {{ selected }}</span>
new Vue({
el: '...',
data: {
selected: 'A',
options: [
{ text: 'One', value: 'A' },
{ text: 'Two', value: 'B' },
{ text: 'Three', value: 'C' }
]
}
})
自定義指令
在js里面定義指令
// 注冊一個全局自定義指令 `v-focus`
Vue.directive('focus', {
// 當被綁定的元素插入到 DOM 中時……
inserted: function (el) {
// 聚焦元素
el.focus()
}
})
在組件中使用指令
<input v-focus>
directives: {
focus: {
// 指令的定義
inserted: function (el) {
el.focus()
}
}
}
指令的鉤子函數、參數及更高級用法參考:指令使用
vue2+vuex+axios全家桶集成
vue中一般使用vuex來進行數據共享,axios來獲取數據。
安裝vuex
npm install vuex --save-dev
。
在項目的入口js文件main.js中:
import Vue from 'vue'
import Vuex from 'vuex'
import App from './App'
import router from './router'
import store from './store/index'
Vue.config.productionTip = false
Vue.use(Vuex)
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
store,
template: '<App/>',
components: { App }
})
新建一個/store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
// 定義狀態
state: {
author: 'xxx'
},
mutations: {
changeAuthor: function (state, newValue) {
state.author = newValue
}
}
})
export default store
在組件中使用
{{$store.state.author}}
xx = this.$store.state.author
this.$store.state.author = xx
this.$store.commit('changeAuthor','xx');
安裝axios
npm install axios --save-dev
。
axios使用,這里只使用了get方法,其他的可以查看axios使用說明
import axios from 'axios'
axios.get('static/data.json').then((res) => {
this.$store.commit('changeAuthor',res.data.author);
});
路由
vue-cli 已經集成了vue-router,在這里就直接按照官方的步驟來實踐一下。
在router/index.js里面配置好路由信息。
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'
import Dashboard from '@/components/Dashboard'
import Home from '@/components/Home'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'HelloWorld',
component: HelloWorld
}, {
path: '/home',
name: 'Home',
component: Home
}, {
path: '/dashboard',
name: 'Dashboard',
component: Dashboard
}
]
})
在main.js中引入路由,在App.vue中加入
<router-view/>
便可以在這個位置顯示路由相對應的組件。
通過
<router-link to="/home">go to home</router-link>
可以進行路由的跳轉。
動態路由
類似于angularjs,vue-router也可以通過如下方式來實現動態路由。
path: '/user/:id', component: User
,此時id將被綁定在this.$route.params
中,在組件中通過this.$route.params.id
來獲取值。
在組件中使用$route會使之與其對應路由形成高度耦合,從而使組件只能在某些特定的url上使用,限制了其靈活性。
使用props將組件和路由解耦:
// 路由中定義,增加props屬性并設為true。
{ path: '/user/:id', component: User, props: true }
// 在組件中定義及使用,
props: ['id'],
template: '<div>User {{ id }}</div>'
嵌套路由
vue中嵌套路由需要使用children屬性來實現,嵌套路由的用法如下:
{
path: '/hello',
name: 'HelloWorld',
component: HelloWorld,
children: [
{
path: 'test',
component: Test
}
]
}
在HelloWorld中需要添加
<router-view/>
,要注意,以 / 開頭的嵌套路徑會被當作根路徑。
編程式導航
除了在html中通過
<router-link to>
來進行路由跳轉之外,在javascript中,我們還可以使用this.$router.push
來進行跳轉。
// 字符串
router.push('home')
// 對象
router.push({ path: 'home' })
// 命名的路由
router.push({ name: 'user', params: { userId: 123 }})
// 帶查詢參數,變成 /register?plan=private
router.push({ path: 'register', query: { plan: 'private' }})
//注意:如果提供了 path,params 會被忽略,上述例子中的 query 并不屬于這種情況。取而代之的是下面例子的做法,你需要提供路由的 name 或手寫完整的帶有參數的 path:
const userId = 123
router.push({ name: 'user', params: { userId }}) // -> /user/123
router.push({ path: `/user/${userId}` }) // -> /user/123
// 這里的 params 不生效
router.push({ path: '/user', params: { userId }}) // -> /user
router.replace(location, onComplete?, onAbort?)
,跟 router.push 很像,唯一的不同就是,它不會向 history 添加新記錄,而是跟它的方法名一樣 —— 替換掉當前的 history 記錄。
router.go(n)
,這個方法的參數是一個整數,意思是在 history 記錄中向前或者后退多少步,類似 window.history.go(n)。
參數傳遞
除了上面通過路徑傳遞的方式之外,還可以使用query來進行參數傳遞,對比如下:
//路徑的方式
{
path: '/home/:searchKey',
name: 'Home',
component: Home
}
<router-link :to="{name: 'Home', params:{searchKey:'better'}}">
this.$router.push({name:'Home' ,params:{searchKey:'better'}});
console.log(this.$route.params.searchKey);
// 結果 .../home/better
//參數的方式
{
path: '/home',
name: 'Home',
component: Home
}
<router-link :to="{name: 'Home',query:{searchKey:'better'}}">
this.$router.push({name:'Home' ,query:{searchKey:'better'}});
console.log(query:this.$route.query.searchKey);
// 結果 .../home?searchKey=better
路由重定向和別名
例子:
{
path: '/dashboard',
name: 'Dashboard',
component: Dashboard,
alias: '/bb'
}, {
path: '/aa',
redirect: '/dashboard'
}
訪問/aa將跳轉到/dashboard,訪問/bb等價于/dashboard。vue使用redirect進行重定向,使用alias來設置別名。
路由守衛
全局守衛,注意需要增加next(),才會進行下面其他的路由守衛。
router.beforeEach((to, from, next) => {
console.log('全局路由守護');
// next('/hello');
next();
})
每個守衛方法接收三個參數:
to: Route: 即將要進入的目標 路由對象
from: Route: 當前導航正要離開的路由
next: Function: 一定要調用該方法來 resolve 這個鉤子。執行效果依賴 next 方法的調用參數。
next(): 進行管道中的下一個鉤子。如果全部鉤子執行完了,則導航的狀態就是 confirmed (確認的)。
next(false): 中斷當前的導航。如果瀏覽器的 URL 改變了(可能是用戶手動或者瀏覽器后退按鈕),那么 URL 地址會重置到 from 路由對應的地址。
next('/') 或者 next({ path: '/' }): 跳轉到一個不同的地址。當前的導航被中斷,然后進行一個新的導航。
next(error): (2.4.0+) 如果傳入 next 的參數是一個 Error 實例,則導航會被終止且該錯誤會被傳遞給 router.onError() 注冊過的回調。
在單個路由里面配置路由守衛,參數同上。
{
path: '/hello',
name: 'HelloWorld',
component: HelloWorld,
beforeEnter: (to, from, next) => {
console.log('hello 路由守護');
},
children: [
{
path: 'test',
component: Test
}
]
}
vue 鉤子測試及各種配置用法
<template>
<div class="content">
</div>
</template>
<script>
export default {
name: 'Shop',
data() {
return {
a: 1
}
},
computed: {
//計算屬性
// 僅讀取
aDouble: function() {
return this.a * 2
},
// 讀取和設置
aPlus: {
get: function() {
return this.a + 1
},
set: function(v) {
this.a = v - 1
}
}
},
methods: {
chirdSay: function() {
alert("hello");
}
},
watch: {
a: function(val, oldVal) {
console.log('new: %s, old: %s', val, oldVal)
}
},
beforeCreate() {
//組件創建前調用
console.log("beforeCreate");
},
created() {
//組件創建完成后調用,才能調用組件的屬性,方法。
this.aPlus = 33;
console.log(this.aPlus);
console.log("created");
},
beforeMount() {
console.log("beforeMount");
},
mounted() {
console.log("mounted");
},
beforeUpdate() {
console.log("beforeUpdate");
},
updated() {
console.log("updated");
},
activated() {
console.log("activated");
},
deactivated() {
console.log("deactivated");
},
beforeDestroy() {
console.log("beforeDestroy");
},
beforeRouteEnter(to, from, next) {
// 在渲染該組件的對應路由被 confirm 前調用
// 不!能!獲取組件實例 `this`
// 因為當守衛執行前,組件實例還沒被創建
console.log("beforeRouteEnter");
next(vm => {
vm.$store.state.title = '點餐平臺';
});
},
beforeRouteUpdate(to, from, next) {
// 在當前路由改變,但是該組件被復用時調用
// 舉例來說,對于一個帶有動態參數的路徑 /foo/:id,在 /foo/1 和 /foo/2 之間跳轉的時候,
// 由于會渲染同樣的 Foo 組件,因此組件實例會被復用。而這個鉤子就會在這個情況下被調用。
// 可以訪問組件實例 `this`
console.log("beforeRouteUpdate");
next();
},
beforeRouteLeave(to, from, next) {
// 導航離開該組件的對應路由時調用
// 可以訪問組件實例 `this`
console.log("beforeRouteLeave");
next();
}
}
</script>
<style>
.el-icon-star-on {
font-size: 4px;
margin-right: 0px;
}
</style>
組件實現調用beforeRouteEnter,通過
next(vm = > {})
在組件mounted完成后可以訪問組件實例,然后調用beforeCreate,此時組件還未初始化,不能調用組件的屬性。接著調用created,初始化組件完畢,可以使用組件實例。接著調用beforeMount》mounted進行掛載前和掛載的邏輯,掛載不太明白,掠過。之后next(vm = > {})
里面的語句倍調用。離開時調用beforeRouteLeave》beforeDestroy。
其他
- $nextTick,頁面元素加載完后調用。有時候使用v-for等動態加載html的時候,獲取對應元素會出現undefined,此時使用$nextTick可以在加載完后再進行取值。
this.$nextTick(function() {
})
- $refs,獲取dom元素。
//html
<div class="scroll" ref="list">
//javascript
console.log(this.$refs.list);
//滾動條操作。
this.$refs.list.clientHeight //獲取元素高度
this.$refs.list.addEventListener('scroll', function(){}); //監聽滾動事件
this.$refs.list.scrollTo(0, height); //使滾動條滾動到對應位置。
this.$refs.list.scrollTop; //當前滾動條位置