熱重載:(hot-reloading)你修改頁面之后憋肖,自動刷新,不用手動刷新
vue-cli的配置參考 (待仔細(xì)學(xué)習(xí))
學(xué)習(xí)傳送門:https://cli.vuejs.org/zh/config/#%E5%85%A8%E5%B1%80-cli-%E9%85%8D%E7%BD%AE
webpack配置(待仔細(xì)學(xué)習(xí))
學(xué)習(xí)傳送門:https://webpack.js.org/configuration/
過渡&動畫 (要仔細(xì)學(xué)習(xí),又是一個門)
學(xué)習(xí)傳送門:https://cn.vuejs.org/v2/guide/transitions.html
通過插槽分發(fā)內(nèi)容我抠、動態(tài)組件、解析Dom模板時的注意事項
學(xué)習(xí)傳送門:https://cn.vuejs.org/v2/guide/components.html#%E9%80%9A%E8%BF%87%E6%8F%92%E6%A7%BD%E5%88%86%E5%8F%91%E5%86%85%E5%AE%B9
用key管理可復(fù)用的元素
切換input的時候,input就是獨(dú)立的了
v-if vs v-show
傳送門:http://www.reibang.com/p/2feb8cad6abf
對象變更檢測注意事項
var vm=new Vue({
data:{
userProfile:{
name:'anika'
}
}
})
//如果想要給userProfile添加一個屬性age砸紊,操作如下
Vue.set(vm.userProfile,'age',27)
或者
vm.$set(vm.userProfile,'age',27)
如果想要給已知的對象賦值多個屬性可以這樣操作
Object.assign({},vm.userProfile,{
age:27,
favoriteColor:'vue Green'
})
計算屬性的應(yīng)用
<li v-for="n in evenNumber"> {{n}}</li>
<script>
new vue({
data():{
numbers:[1,2,3,4],
computed:{
evenNumber:function(){
return this.numbers.filter(function(number){
return number%2===0
})
}
}
}
})
</script>
或者
<li v-for="n in even(numbers)">{{ n }}</li>
data: {
numbers: [ 1, 2, 3, 4, 5 ]
},
methods: {
even: function (numbers) {
return numbers.filter(function (number) {
return number % 2 === 0
})
}
}
v-for在組件中的應(yīng)用
<ul>
<li
is="todo-item"
v-for="(todo, index) in todos"
v-bind:key="todo.id"
v-bind:title="todo.title"
v-on:remove="todos.splice(index, 1)"
></li>
</ul>
因為li時ul內(nèi)部的有效元素谊路,所以通過is="todo-item"的方式在ul中去循環(huán)
這樣循環(huán)讹躯,組件內(nèi)部中需要通過prop去接收item傳遞的值:例如:
Vue.component('todo-item', {
template: '\
<li>\
{{ title }}\
<button v-on:click="$emit(\'remove\')">Remove</button>\
</li>\
',
props: ['title']
})
事件修飾符
傳送門:https://cn.vuejs.org/v2/guide/events.html
.stop阻止單擊事件繼續(xù)傳播 可以阻止冒泡行為
<div v-on:click="doThis1" class="btn">外部元素
<div class="s-btn" v-on:click.stop="doThis">
內(nèi)部元素
</div>
</div>
<script>
export default{
name:'test',
methods:{
doThis(){
console.log('我是內(nèi)部元素')
},
doThis1(){
console.log('我是外部元素')
}
}
}
</script>
.capture修飾符事件捕獲模式
如果有此修飾符會先再此先處理,然后內(nèi)部的監(jiān)聽事件才會進(jìn)行處理
例如:
<div v-on:click.capture="doThis2" class="btn3">
1366777777777777777
<div class="btn2" v-on:click="doThis3">
134
</div>
</div>
doThis2(){
console.log('我是1')
},
doThis3(){
console.log('我是2')
}
輸出結(jié)果為:由外向內(nèi)
反之:如果沒有.capture修飾符輸出結(jié)果為:由內(nèi)向外
.self只有當(dāng)點(diǎn)擊的是當(dāng)前本身的時候才會觸發(fā)click事件
例如:
代碼如下: other在self內(nèi)部缠劝,當(dāng)點(diǎn)擊self的時候潮梯,輸出結(jié)果為
<div v-on:click.capture="doThis2" class="btn3">
1366777777777777777
<div class="btn2" v-on:click="doThis3">
134
<div v-on:click.self="doThat">
self
<div v-on:click="doThat1">
other
</div>
</div>
</div>
</div>
沒有self
注意:使用順序,
例如:v-on:click.prevent.self 會阻止所有的點(diǎn)擊惨恭,
v-on:click.self.prevent會阻止對元素自身的點(diǎn)擊
.passive修飾符尤其能夠提供移動端的性能
<div class="click" v-on:scroll.passive="onScroll" >
</div>
不會等待scroll監(jiān)聽器執(zhí)行完再去執(zhí)行Onscroll,他會立即執(zhí)行秉馏,因為.passive永遠(yuǎn)不會調(diào)用阻止默認(rèn)行為的方法event.preventDefault()
表單輸入綁定
注意
1.input中的v-mobel這樣綁定,不會在輸入法組合文字的過程中更新例子如下:
如果想要即時更新的脱羡,需要使用@input去實現(xiàn)
<input v-model="message" placeholder="edit me">
2.在textarea文本域中
(<textarea>{{text}}</textarea>) //并不會生效
//應(yīng)該使用v-mobel替換
<textarea v-model="message" placeholder="add multiple lines"></textarea>
3.select選擇框
如果v-mobel表達(dá)式的初始值未能匹配任何選項萝究,<select>元素將被渲染為"未選中"狀態(tài)免都。
在ios中,這會使用戶無法選擇第一個選項帆竹。因為這樣的情況下绕娘,ios不會觸發(fā)change事件。因此栽连,更推薦像上面這樣提供一個值為空的禁用選項
4.修飾符 .lazy
<input v-model.lazy="msg" >只有在input失去焦點(diǎn)或者按回車的時候才會被賦值
插槽的使用
動態(tài)組件&異步組件
動態(tài)組件的應(yīng)用:
- 多標(biāo)簽的界面中
- 可以運(yùn)用keep-live標(biāo)簽來保持原來的狀態(tài)
- 實際應(yīng)用中的商品詳情頁面业舍,可能會有多種狀態(tài),拼團(tuán)升酣,搶購舷暮,默認(rèn),等可以通過動態(tài)組件實現(xiàn)噩茄,不用好多if語句判斷了
<keep-alive>
<component v-bind:is="currentTabComponent"></component>
</keep-alive>
異步組件應(yīng)用:
去請求的時候
定義組件名
- kebab-case
-PasecalCase
注意:在使用自定義組件的時候下面,最好使用kebab-case的方式
基礎(chǔ)組件的自動化全局注冊:有些不解,希望有人指教
prop
注意:
在子組件中通過props定義名字的使用的駝峰绩聘,在運(yùn)用綁定傳遞的時候需要用kebab-case
Vue.component('blog-post',{
props:['postTitle'],
templete:'<h3>{{postTitle}}</h3>'
})
//
<blog-post post-title="hello"></blog-post>
prop類型
當(dāng)只在prop中寫字符串的時候為數(shù)組類型
prop['title','likes','isPublished','commentIds','author']
當(dāng)在prop中寫類型的時候為對象類型
prop{
title:String,
likes:Number,
isPublished:Boolean,
commentIds:Array,
author:Object
}
prop的傳遞有靜態(tài)類型和動態(tài)類型
//靜態(tài)類型
<blog-post title="My journey with Vue"></blog-post>
//動態(tài)類型
<blog-post v-bind:title="post.title"></blog-post>
當(dāng)傳遞一個靜態(tài)類型的數(shù)字沥割、對象、布爾類型的時候需要通過v-bind方式傳遞凿菩,告訴vue這不是一個字符串机杜,這是一個javascript表達(dá)式
<blog-post v-bind:likes="42"></blog-post>
<blog-post is-published></blog-post>//默認(rèn)傳遞的是true
<blog-post v-bind:is-published="false"></blog-post>
...
單向數(shù)據(jù)流
父級prop的更新會向下流動到子組件中,但是反過來不行衅谷。這樣會防止子組件意外改變父級組件的狀態(tài)椒拗。
子組件改變父組件的值需要通過this.$emit('值',方法名)的方式更改,在組件中:方法名:父組件在定義一個方法名
prop驗證
Vue.component('my-component', {
props: {
// 基礎(chǔ)的類型檢查 (`null` 和 `undefined` 會通過任何類型驗證)
propA: Number,
// 多個可能的類型
propB: [String, Number],
// 必填的字符串
propC: {
type: String,
required: true
},
// 帶有默認(rèn)值的數(shù)字
propD: {
type: Number,
default: 100
},
// 帶有默認(rèn)值的對象
propE: {
type: Object,
// 對象或數(shù)組默認(rèn)值必須從一個工廠函數(shù)獲取
default: function () {
return { message: 'hello' }
}
},
// 自定義驗證函數(shù)
propF: {
validator: function (value) {
// 這個值必須匹配下列字符串中的一個
return ['success', 'warning', 'danger'].indexOf(value) !== -1
}
}
}
})
注意:那些prop會在一個組件實例創(chuàng)建之前進(jìn)行驗證获黔,所以實例屬性如(data蚀苛、computed等)再default或validator函數(shù)中不可用
自定義事件
.sync修飾符
在有些情況下,我們可能需要對一個prop進(jìn)行“雙向綁定”玷氏。也就是子組件可以修改父組件的問題
普通的this.$emit
myShow(){
this.$emit('fun','修改的值傳遞')
}
//父組件
<mycom v-on:fun="change"></mycom>
使用.sync修飾符
closeDiv() {
this.$emit('update:show', false); //觸發(fā) input 事件堵未,并傳入新值
}
//父組件
<myComponent :show.sync='valueChild' style="padding: 30px 20px 30px 5px;border:1px solid #ddd;margin-bottom: 10px;"></myComponent>
<button @click="changeValue">toggle</button>
//當(dāng)前中的valueChild就是子組件傳遞過來的值,不用父組件在創(chuàng)建方法接收
export default{
data(){
return{
valueChild:true,
}
},
methods:{
changeValue(){
this.valueChild = !this.valueChild
}
}
}
注意:.sync后面不能是表達(dá)式
插槽:https://cn.vuejs.org/v2/guide/components-slots.html
處理邊界情況
訪問子組件實例盏触,或子元素可以通過ref
<base-input ref="usernameInput"></base-input>
this.$refs.usernameInput
注意:refs
依賴注入
意思就是如果子元素想要訪問父組件中的方法
在父組件中
provide: function () {
return {
getMap: this.getMap
}
}
在子組件中
inject: ['getMap']
注意:provide 和 inject 主要在開發(fā)高階插件/組件庫時使用赞辩。并不推薦用于普通應(yīng)用程序代碼中雌芽。
程序化的事件監(jiān)聽
- $on(eventName,eventHandler)監(jiān)聽一個事件
- $once(eventName,eventHandler) 一次性監(jiān)聽一個事件
- $off(eventName,eventHander) 停止監(jiān)聽一個事件
應(yīng)用場景:
使用第三方庫的時候,例如時間插件
//平常我們使用
// 一次性將這個日期選擇器附加到一個輸入框上
// 它會被掛載到 DOM 上诗宣。
mounted: function () {
// Pikaday 是一個第三方日期選擇器的庫
this.picker = new Pikaday({
field: this.$refs.input,
format: 'YYYY-MM-DD'
})
},
// 在組件被銷毀之前膘怕,
// 也銷毀這個日期選擇器想诅。
beforeDestroy: function () {
this.picker.destroy()
}
潛在問題
需要在在這個組件實例中保存這個picker
難于程序化清理
通過程序化監(jiān)聽器解決召庞,并且可以引用使用多個時間三方庫
mounted: function () {
var picker = new Pikaday({
field: this.$refs.input,
format: 'YYYY-MM-DD'
})
this.$once('hook:beforeDestroy', function () {
picker.destroy()
})
}
mounted: function () {
this.attachDatepicker('startDateInput') //不同的ref 元素岛心,使用picker
this.attachDatepicker('endDateInput')
},
methods: {
attachDatepicker: function (refName) {
var picker = new Pikaday({
field: this.$refs[refName],
format: 'YYYY-MM-DD'
})
this.$once('hook:beforeDestroy', function () {
picker.destroy()
})
}
}
組件之間的循環(huán)引用
例子:
<p>
<span>{{ folder.name }}</span>
<tree-folder-contents :children="folder.children"/>
</p>
tree-folder-contents 組件
<ul>
<li v-for="child in children">
<tree-folder v-if="child.children" :folder="child"/>
<span v-else>{{ child.name }}</span>
</li>
</ul>
需要在注冊組件的時候這樣引入:使用webpack的異步import
components: {
TreeFolderContents: () => import('./tree-folder-contents.vue')
}
組件之間循環(huán)引用的問題所在:(沒看懂,請大佬指教)
- 為了解釋這里發(fā)生了什么篮灼,我們先把兩個組件稱為 A 和 B忘古。模塊系統(tǒng)發(fā)現(xiàn)它需要 A,但是首先 A 依賴 B诅诱,但是 B 又依賴 A髓堪,但是 A 又依賴 B,如此往復(fù)娘荡。這變成了一個循環(huán)干旁,不知道如何不經(jīng)過其中一個組件而完全解析出另一個組件。為了解決這個問題炮沐,我們需要給模塊系統(tǒng)一個點(diǎn)争群,在那里“A 反正是需要 B 的,但是我們不需要先解析 B大年』槐。”
通過v-once 創(chuàng)建低開銷的靜態(tài)組件
ue.component('terms-of-service', {
template: `
<div v-once>
<h1>Terms of Service</h1>
... a lot of static content ...
</div>
`
})
注意:
當(dāng)你需要渲染大量靜態(tài)內(nèi)容時,極少數(shù)的情況下它會給你帶來便利翔试,除非你非常留意渲染變慢了轻要,不然它完全是沒有必要的——再加上它在后期會帶來很多困惑。例如垦缅,設(shè)想另一個開發(fā)者并不熟悉 v-once 或漏看了它在模板中冲泥,他們可能會花很多個小時去找出模板為什么無法正確更新。
混入
例子:
var mixin = {
data: function () {
return {
message: 'hello',
foo: 'abc'
}
}
}
new Vue({
mixins: [mixin],
data: function () {
return {
message: 'goodbye',
bar: 'def'
}
},
created: function () {
console.log(this.$data)
// => { message: "goodbye", foo: "abc", bar: "def" }
}
})
遇到?jīng)_突的data變量和方法名的策略
- 同名的data變量優(yōu)先選擇組件內(nèi)部的
- 同名的鉤子函數(shù)會合并成一個數(shù)組例如created 都會執(zhí)行
var mixin = {
created: function () {
console.log('混入對象的鉤子被調(diào)用')
}
}
new Vue({
mixins: [mixin],
created: function () {
console.log('組件鉤子被調(diào)用')
}
})
// => "混入對象的鉤子被調(diào)用"
// => "組件鉤子被調(diào)用"
- 值為對象的選項壁涎,例如 method柏蘑、components、 directives 將被合并為同一個對象粹庞。兩個兌現(xiàn)鍵值對沖突的時候優(yōu)先組件內(nèi)部的
var mixin = {
methods: {
foo: function () {
console.log('foo')
},
conflicting: function () {
console.log('from mixin')
}
}
}
var vm = new Vue({
mixins: [mixin],
methods: {
bar: function () {
console.log('bar')
},
conflicting: function () {
console.log('from self')
}
}
})
vm.foo() // => "foo"
vm.bar() // => "bar"
vm.conflicting() // => "from self"
過濾器
<!-- 在雙花括號中 -->
{{ message | capitalize }}
<!-- 在 `v-bind` 中 -->
<div v-bind:id="rawId | formatId"></div>
自定義過濾器:
filters: {
capitalize: function (value) {
if (!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
}
}
或者
Vue.filter('capitalize', function (value) {
if (!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
})
new Vue({
// ...
})
當(dāng)全局過濾器和局部過濾器重名時咳焚,采用局部過濾器
//過濾器可以串聯(lián)
{{ message | filterA | filterB }}
//先將message傳入到filterA方法中,然后再把篩選后的結(jié)果傳入到filterB方法中進(jìn)行篩選
還可以這樣:
因為過濾器是javascript函數(shù)庞溜,因此可以接收參數(shù)
{{ message | filterA('arg1', arg2) }}
單元測試 (不明白革半,需要學(xué)習(xí))
安全
第一原則:永遠(yuǎn)不要使用不可信任得模板
這樣得做就等于允許在應(yīng)用程序中執(zhí)行任意得javascript,更糟得是如果在服務(wù)端渲染得話可能導(dǎo)致服務(wù)器被攻破流码。
例子:
new Vue({
el: '#app',
template: `<div>` + userProvidedString + `</div>` // 永遠(yuǎn)不要這樣做
})
vue的安全措施:https://cn.vuejs.org/v2/guide/security.html
Html內(nèi)容
不論使用模板還是渲染函數(shù)又官,內(nèi)容都會被自動轉(zhuǎn)義。
例如:
<h1>{{ userProvidedString }}</h1>
如果userProvideString變量中包含了
'<script>alert("hi")</script>' //被注入的惡意代碼
則會被轉(zhuǎn)義成
<script>alert("hi")</script> //沒有辦法執(zhí)行script了
因此避免了腳本注入漫试。該轉(zhuǎn)義通過諸如textContent的瀏覽原生的Api完成六敬,所以除非瀏覽器本身存在安全漏洞,否則不會存在安全漏洞
Attribute(屬性)綁定 道理同
<h1 v-bind:title="userProvidedString">
hello
</h1>
如果 userProvidedString 包含了:
'" onclick="alert(\'hi\')'//惡意代碼
則會被轉(zhuǎn)義成為如下 HTML:
" onclick="alert('hi')
潛在危險
注入url
<a v-bind:href="userProvidedUrl">
click me
</a>
如果沒有對url進(jìn)行過濾“防止javascript:通過”則會由潛在的安全問題驾荣。有些庫可以幫助解決
sanitize-url
https://www.npmjs.com/package/@braintree/sanitize-url
注意:在前端進(jìn)行了url過濾外构,那么久已經(jīng)有安全問題了普泡。用戶提供的url永遠(yuǎn)需要通過后端在入庫之前進(jìn)行過濾。然后這個被注入的問題在每個客戶端連接api時就會被阻止审编,包括原生移動應(yīng)用撼班。
注入樣式
<a
v-bind:href="sanitizedUrl"
v-bind:style="userProvidedStyles"
>
click me
</a>
假設(shè)sanitizedUrl已經(jīng)被過濾過了。但通過userProvidedStyles垒酬,惡意用戶仍然可以提供css來進(jìn)行“點(diǎn)擊詐騙”砰嘁。將鏈接的樣式設(shè)置為一個透明的方框覆蓋在“登陸”上,然后再把按鈕做成應(yīng)用中的按鈕的樣子勘究。它們就可以獲取一個用戶真實的登陸信息矮湘。
可以改為:
<a
v-bind:href="sanitizedUrl"
v-bind:style="{
color: userProvidedColor,
background: userProvidedBackground
}"
>
click me
</a>
推薦使用對象語法且只允許用戶提供特定的可以安全控制的property的值
深入響應(yīng)式原理
當(dāng)把一個javascript對象傳入vue實例作為data選項,vue將遍歷此對象所有的屬性口糕,并使用Object.defineProperty把這些屬性全部轉(zhuǎn)換為getter/setter.Object.defineProoperty是es5中一個無法shim的特性板祝。是他們能夠讓vue追蹤依賴。再屬性被訪問和修改時通知變更走净。
每一個實例都對應(yīng)一個watcher實例券时,他會再組件渲染過程中把接觸過的數(shù)據(jù)屬性記錄為依賴。之后當(dāng)依賴的setter觸發(fā)時伏伯,會通知watcher橘洞,從而使它關(guān)聯(lián)的組件更新渲染。
檢測變化的注意事項
Vue不能檢測數(shù)組和對象的變化说搅。
對象
vue無法檢測property的添加和移除炸枣。
vue會在初始化實例時對屬性執(zhí)行g(shù)etter/setter轉(zhuǎn)化,所以屬性必須在data上存在才能讓vue將它轉(zhuǎn)換為響應(yīng)式弄唧。
對于已經(jīng)創(chuàng)建得vue實例适肠,vue不允許動態(tài)添加根級別得響應(yīng)式
可以使用Vue.set(object,propertyName,value)
例如:
Vue.set(vm.someObject, 'b', 2)
或者
this.$set(this.someObject,'b',2)
有的時候可能需要為已有對象賦值多個新屬性,比如使用Object.assign()或_.extend() 候引。但是這樣添加到對象上得新屬性不會觸發(fā)更新侯养。沒有被監(jiān)聽到,添加這些屬性澄干」淇可以這樣,用原對象與要混合進(jìn)去得對象得屬性一起創(chuàng)建一個新的對象麸俘。
this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 })
數(shù)組
vue不能檢測的數(shù)組變動:
1.當(dāng)你利用索引直接設(shè)置一個數(shù)組項時:
vm.items[indexOfItem] = newValue
2.當(dāng)你修改數(shù)組的長度時
vm.items.length = newLength
舉例:
var vm = new Vue({
data: {
items: ['a', 'b', 'c']
}
})
vm.items[1] = 'x' // 不是響應(yīng)性的
vm.items.length = 2 // 不是響應(yīng)性的
可以觸發(fā)更新的修改數(shù)組方式:
Vue.set(vm.items, indexOfItem, newValue)||vm.$set(vm.items, indexOfItem, newValue)
解決數(shù)組的長度
vm.items.splice(newLength)
異步更新隊列
vue在更新dom時是異步執(zhí)行的辩稽。只要偵聽到數(shù)據(jù)變化,vue將開啟一個隊列从媚,vue將開啟一個隊列逞泄,并緩沖在同一事件循環(huán)中發(fā)生的所有數(shù)據(jù)變更。如果同一個watcher被多次觸發(fā),只會被堆入到隊列中一次喷众。
vue在內(nèi)部對異步隊列嘗試使用原生的 Promise.then 各谚、MutationObserver和setImmediate,如果執(zhí)行環(huán)境不支持侮腹,則會采用setTimeout(fn,0)
var vm = new Vue({
el: '#example',
data: {
message: '123'
}
})
vm.message = 'new message' // 更改數(shù)據(jù)
vm.$el.textContent === 'new message' // false(這個時候還沒更改完成)
//只有在執(zhí)行下一個事件循環(huán)tick中更新
Vue.nextTick(function () {
vm.$el.textContent === 'new message' // true
})
或者
Vue.component('example', {
template: '<span>{{ message }}</span>',
data: function () {
return {
message: '未更新'
}
},
methods: {
updateMessage: function () {
this.message = '已更新'
console.log(this.$el.textContent) // => '未更新'
this.$nextTick(function () {
console.log(this.$el.textContent) // => '已更新'
})
}
}
})
因為$necxtTick()返回一個Promise對象,所以你可以使用新的es7的語法糖
async/await語法完成相同的事情
async 聲明一個異步的function
await 等待一個異步方法執(zhí)行完成 再去執(zhí)行下面的代碼
methods: {
updateMessage: async function () {
this.message = '已更新'
console.log(this.$el.textContent) // => '未更新'
await this.$nextTick() 等待
console.log(this.$el.textContent) // => '已更新'
}
}
在mounted鉤子函數(shù)默認(rèn)是this.$el.document獲取不到
//可以這樣
mounted: function () {
this.$nextTick(function () {
// 代碼保證 this.$el 在 document 中
})
}
注意:
在created鉤子中通過$on綁定了事件稻励,最好在組件銷毀前父阻,清除事件的監(jiān)聽
beforeDestroy: function () {
eventHub.$off('add-todo', this.addTodo)
eventHub.$off('delete-todo', this.deleteTodo)
},
vue和其它框架的對比:http://www.reibang.com/p/eef0a8e0dc49
vue打包得時候總是出現(xiàn)好多js,希望只出現(xiàn)一個js
因為頁面比較多望抽,打包成一個js的話加矛,文件會很大。否則第一次加載的時候半天都是白屏煤篙,一般都是按需加載斟览,打包也是根據(jù)需要進(jìn)行打包