總結一些vue中必知必會的知識點,讓咱們在面試以前可以成竹在胸一點~javascript
方法一:propscss
<template>
<child :msg="message"></child>
</template>
複製代碼
<template>
<div>{{msg}}</div>
</template>
<script>
export default {
props: {
msg: {
type: String,
required: true
}
}
}
</script>
複製代碼
方法二:使用$childrenhtml
使用$children
能夠在父組件中訪問子組件。好比調用子組件的方法,並傳入值等。vue
方法一:使用vue事件java
父組件向子組件傳遞事件方法,子組件經過$emit
觸發事件,回調給父組件。node
<template>
<child @msgFunc="func"></child>
</template>
<script>
import child from './child.vue';
export default {
components: {
child
},
methods: {
func (msg) {
console.log(msg); // ssss
}
}
}
</script>
複製代碼
<template>
<button @click="handleClick">點我</button>
</template>
<script>
export default {
props: {
msg: {
type: String,
required: true
}
},
methods () {
handleClick () {
//........
this.$emit('msgFunc', 'ssss');
}
}
}
</script>
複製代碼
方法二:使用$parent面試
使用$parent
能夠訪問父組件的實例,固然也就能夠訪問父組件的屬性和方法了。express
非父子組件通訊,Vue官方推薦使用一個Vue實例做爲中央事件總線。數組
意思就是將一個公共狀態保存在一個這些組件共用的一個父組件上,這樣就可使用子組件通訊父組件的方式來間接的完成通訊了。瀏覽器
vue入口源碼以下:
import { initMixin } from './init'
import { stateMixin } from './state'
import { renderMixin } from './render'
import { eventsMixin } from './events'
import { lifecycleMixin } from './lifecycle'
import { warn } from '../util/index'
function Vue (options) {
if (process.env.NODE_ENV !== 'production' &&
!(this instanceof Vue)
) {
warn('Vue is a constructor and should be called with the `new` keyword')
}
this._init(options)
}
initMixin(Vue)
// initMixin給Vue.prototype添加:
// _init函數,
stateMixin(Vue)
/* stateMixin給Vue.prototype添加: $data屬性, $props屬性, $set函數, $delete函數, $watch函數, ... */
eventsMixin(Vue)
/* eventsMixin給Vue.prototype添加: $on函數, $once函數, $off函數, $emit函數, $watch方法, ... */
lifecycleMixin(Vue)
/* lifecycleMixin給Vue.prototype添加: _update方法:私有方法,用於更新dom,其中調用_patch產生跟新後的dom, $forceUpdate函數, $destroy函數, ... */
renderMixin(Vue)
/* renderMixin給Vue.prototype添加: $nextTick函數, _render函數, ... */
export default Vue
複製代碼
vue源碼加載的時候,也就是在new Vue()以前作了一些初始化工做。
new Vue的時候會執行一個_init方法。代碼以下:
export function initMixin (Vue: Class<Component>) {
Vue.prototype._init = function (options?: Object) {
const vm: Component = this
// a uid
vm._uid = uid++
let startTag, endTag
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
startTag = `vue-perf-start:${vm._uid}`
endTag = `vue-perf-end:${vm._uid}`
mark(startTag)
}
// a flag to avoid this being observed
vm._isVue = true
// merge options
if (options && options._isComponent) {
// optimize internal component instantiation
// since dynamic options merging is pretty slow, and none of the
// internal component options needs special treatment.
initInternalComponent(vm, options)
} else {
vm.$options = mergeOptions(
resolveConstructorOptions(vm.constructor),
options || {},
vm
)
}
/* istanbul ignore else */
if (process.env.NODE_ENV !== 'production') {
initProxy(vm)
} else {
vm._renderProxy = vm
}
// expose real self
vm._self = vm
initLifecycle(vm)
initEvents(vm)
initRender(vm)
callHook(vm, 'beforeCreate')
initInjections(vm) // resolve injections before data/props
initState(vm)
initProvide(vm) // resolve provide after data/props
callHook(vm, 'created')
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
vm._name = formatComponentName(vm, false)
mark(endTag)
measure(`vue ${vm._name} init`, startTag, endTag)
}
if (vm.$options.el) {
vm.$mount(vm.$options.el)
}
}
}
複製代碼
從上面的代碼咱們看見_init
很清淅的幹了幾件事, 合併相關配置, 初始化生命週期,初始化事件中心,初始化渲染,初始化 data、props、computed、watcher 等
由於組件可能被多處使用,但它們的data是私有的,因此每一個組件都要return一個新的data對象,若是共享data,修改其中一個會影響其餘組件。
建立真實DOM的代價高:真實的 DOM 節點 node 實現的屬性不少,而 vnode 僅僅實現一些必要的屬性,相比起來,建立一個 vnode 的成本比較低。
觸發屢次瀏覽器重繪及迴流:使用 vnode ,至關於加了一個緩衝,讓一次數據變更所帶來的全部 node 變化,先在 vnode 中進行修改,而後 diff 以後對全部產生差別的節點集中一次對 DOM tree 進行修改,以減小瀏覽器的重繪及迴流。
虛擬dom因爲本質是一個js對象,所以天生具有跨平臺的能力,能夠實如今不一樣平臺的準確顯示。
Virtual DOM 在性能上的收益並非最主要的,更重要的是它使得 Vue 具有了現代框架應有的高級特性。
首先實例化,而後數據綁定,組件掛在、內容替換。
結合上例子分析Vue的生命週期:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>vue生命週期學習</title>
<script src="https://cdn.bootcss.com/vue/2.4.2/vue.js"></script>
</head>
<body>
<div id="app">
<h1>{{message}}</h1>
</div>
</body>
<script> var vm = new Vue({ el: '#app', data: { message: 'Vue的生命週期' }, beforeCreate: function() { console.group('------beforeCreate建立前狀態------'); console.log("%c%s", "color:red" , "el : " + this.$el); //undefined console.log("%c%s", "color:red","data : " + this.$data); //undefined console.log("%c%s", "color:red","message: " + this.message) }, created: function() { console.group('------created建立完畢狀態------'); console.log("%c%s", "color:red","el : " + this.$el); //undefined console.log("%c%s", "color:red","data : " + this.$data); //已被初始化 console.log("%c%s", "color:red","message: " + this.message); //已被初始化 }, beforeMount: function() { console.group('------beforeMount掛載前狀態------'); console.log("%c%s", "color:red","el : " + (this.$el)); //已被初始化 console.log(this.$el); console.log("%c%s", "color:red","data : " + this.$data); //已被初始化 console.log("%c%s", "color:red","message: " + this.message); //已被初始化 }, mounted: function() { console.group('------mounted 掛載結束狀態------'); console.log("%c%s", "color:red","el : " + this.$el); //已被初始化 console.log(this.$el); console.log("%c%s", "color:red","data : " + this.$data); //已被初始化 console.log("%c%s", "color:red","message: " + this.message); //已被初始化 }, beforeUpdate: function () { console.group('beforeUpdate 更新前狀態===============》'); console.log("%c%s", "color:red","el : " + this.$el); console.log(this.$el); console.log("%c%s", "color:red","data : " + this.$data); console.log("%c%s", "color:red","message: " + this.message); }, updated: function () { console.group('updated 更新完成狀態===============》'); console.log("%c%s", "color:red","el : " + this.$el); console.log(this.$el); console.log("%c%s", "color:red","data : " + this.$data); console.log("%c%s", "color:red","message: " + this.message); }, beforeDestroy: function () { console.group('beforeDestroy 銷燬前狀態===============》'); console.log("%c%s", "color:red","el : " + this.$el); console.log(this.$el); console.log("%c%s", "color:red","data : " + this.$data); console.log("%c%s", "color:red","message: " + this.message); }, destroyed: function () { console.group('destroyed 銷燬完成狀態===============》'); console.log("%c%s", "color:red","el : " + this.$el); console.log(this.$el); console.log("%c%s", "color:red","data : " + this.$data); console.log("%c%s", "color:red","message: " + this.message) } }) </script>
</html>
複製代碼
[1] 在beforeCreate和created鉤子函數之間的生命週期
在這個階段開始初始化事件、而且對數據進行了檢測(劫持),這時候尚未el。[2] created鉤子函數和beforeMount間的生命週期
el: '#app',
(1).若是vue實例對象中有template參數選項,則將其做爲模板編譯成render函數。 (2).若是沒有template選項,則將外部HTML做爲模板編譯。 (3).能夠看到template中的模板優先級要高於outer HTML的優先級。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>vue生命週期學習</title>
<script src="https://cdn.bootcss.com/vue/2.4.2/vue.js"></script>
</head>
<body>
<div id="app">
<!--html中修改的-->
<h1>{{message + '這是在outer HTML中的'}}</h1>
</div>
</body>
<script> var vm = new Vue({ el: '#app', template: "<h1>{{message +'這是在template中的'}}</h1>", //在vue配置項中修改的 data: { message: 'Vue的生命週期' } </script>
</html>
複製代碼
那麼將vue對象中template的選項註釋掉後打印以下信息:
這下就能夠想一想什麼el的判斷要在template以前了~是由於vue須要經過el找到對應的outer template。
在vue對象中還有一個render函數,它是以createElement做爲參數,而後作渲染操做,並且咱們能夠直接嵌入JSX.
new Vue({
el: '#app',
render: function(createElement) {
return createElement('h1', 'this is createElement')
}
})
複製代碼
因此綜合排名優先級: render函數選項 > template選項 > outer HTML.
總之在這個階段就是判斷是否有el,或者template。而後進行處理
[3] beforeMount和mounted 鉤子函數間的生命週期
這段時間就是替換$el,將{{message}}轉爲真正的內容。
[4] mounted
在mounted以前h1中仍是經過{{message}}進行佔位的,由於此時掛在到頁面上的仍是JavaScript中的虛擬DOM形式。在mounted以後能夠看到h1中的內容發生了變化。
[5] beforeUpdate鉤子函數和updated鉤子函數間的生命週期
當vue發現data中的數據發生了改變,會觸發對應組件的從新渲染,前後調用beforeUpdate和updated鉤子函數。
[6] beforeDestroy和destroyed鉤子函數間的生命週期
beforeDestroy鉤子函數在實例銷燬以前調用。在這一步,實例仍然徹底可用。 destroyed鉤子函數在Vue 實例銷燬後調用。調用後,Vue 實例指示的全部東西都會解綁定,全部的事件監聽器會被移除,全部的子實例也會被銷燬。
在代碼的實例中
指令鉤子函數會被傳入如下參數:
el:指令所綁定的元素,能夠用來直接操做 DOM 。
binding:一個對象,包含如下屬性:
- name:指令名,不包括 v- 前綴。
- value:指令的綁定值,例如:v-my-directive="1 + 1" 中,綁定值爲 2。
- oldValue:指令綁定的前一個值,僅在 update 和 componentUpdated 鉤子中可用。不管值是否改變均可用。
- expression:字符串形式的指令表達式。例如 v-my-directive="1 + 1" 中,表達式爲 "1 + 1"。
- arg:傳給指令的參數,可選。例如 v-my-directive:foo 中,參數爲 "foo"。
- modifiers:一個包含修飾符的對象。例如:v-my-directive.foo.bar 中,修飾符對象爲 { foo: true, bar: true }。
- vnode:Vue 編譯生成的虛擬節點。移步 VNode API 來了解更多詳情。
- oldVnode:上一個虛擬節點,僅在 update 和 componentUpdated 鉤子中可用。
複製代碼
問題:在組件中用js動態建立的dom,添加樣式不生效。
複製代碼
<template>
<div class="test"></div>
</template>
<script>
let a=document.querySelector('.test');
let newDom=document.createElement("div"); // 建立dom
newDom.setAttribute("class","testAdd" ); // 添加樣式
a.appendChild(newDom); // 插入dom
</script>
<style scoped>
.test{
background:blue;
height:100px;
width:100px;
}
.testAdd{
background:red;
height:100px;
width:100px;
}
</style>
複製代碼
// test生效 testAdd 不生效
<div data-v-1b971ada class="test"><div class="testAdd"></div></div>
.test[data-v-1b971ada]{ // 注意data-v-1b971ada
background:blue;
height:100px;
width:100px;
}
複製代碼
緣由
當 <style>
標籤有 scoped 屬性時,它的 CSS 只做用於當前組件中的元素。 它會爲組件中全部的標籤和class
樣式添加一個scoped
標識,就像上面結果中的data-v-1b971ada
。 因此緣由就很清楚了:由於動態添加的dom沒有scoped
添加的標識,沒有跟testAdd
的樣式匹配起來,致使樣式失效。
去掉scoped便可。
data() { // data數據
return {
arr: [1,2,3],
obj:{
a: 1,
b: 2
}
};
},
// 數據更新 數組視圖不更新
this.arr[0] = 'OBKoro1';
this.arr.length = 1;
console.log(arr);// ['OBKoro1'];
// 數據更新 對象視圖不更新
this.obj.c = 'OBKoro1';
delete this.obj.a;
console.log(obj); // {b:2,c:'OBKoro1'}
複製代碼
因爲js的限制,Vue 不能檢測以上數組的變更,以及對象的添加/刪除,不少人會由於像上面這樣操做,出現視圖沒有更新的問題。
解決方式:
this.$set(this.arr, 0, "OBKoro1"); // 改變數組
this.$set(this.obj, "c", "OBKoro1"); // 改變對象
複製代碼
Vue能夠監測到數組變化的,數組原生方法:
splice()、 push()、pop()、shift()、unshift()、sort()、reverse()
複製代碼
意思是使用這些方法不用咱們再進行額外的操做,視圖自動進行更新。
過濾器,一般用於後臺管理系統,或者一些約定類型,過濾。Vue過濾器用法是很簡單,可是不少朋友可能都沒有用過。
<!-- 在雙花括號中 -->
{{ message | filterTest }}
<!-- 在 `v-bind` 中 -->
<div :id="message | filterTest"></div>
複製代碼
export default {
data() {
return {
message:1
}
},
filters: {
filterTest(value) {
// value在這裏是message的值
if(value===1){
return '最後輸出這個值';
}
}
}
}
複製代碼
用法就是上面講的這樣,能夠本身在組件中試一試就知道了,很簡單很好用的。
一、v-for循環綁定model:
input在v-for中能夠像以下這麼進行綁定,我敢打賭不少人不知道。
// 數據
data() {
return{
// 這裏設置 key / value
obj: {
ob: "OB",
koro1: "Koro1"
},
// model 存儲
model: {
ob: "默認ob",
koro1: "默認koro1"
}
}
},
// html模板
<div v-for="(value,key) in obj">
<input type="text" v-model="model[key]"> </div>
// input就跟數據綁定在一塊兒了,那兩個默認數據也會在input中顯示
複製代碼
二、v-if儘可能不要與v-for在同一節點使用:
v-for
的優先級比 v-if
更高,若是它們處於同一節點的話,那麼每個循環都會運行一遍v-if。
若是你想根據循環中的每一項的數據來判斷是否渲染,那麼你這樣作是對的:
<li v-for="todo in todos" v-if="todo.type===1">
{{ todo }}
</li>
複製代碼
若是你想要根據某些條件跳過循環,而又跟將要渲染的每一項數據沒有關係的話,你能夠將v-if放在v-for的父節點:
// 數組是否有數據 跟每一個元素沒有關係
<ul v-if="todos.length">
<li v-for="todo in todos">
{{ todo }}
</li>
</ul>
<p v-else>No todos left!</p>
複製代碼
如上,正確使用v-for與v-if優先級的關係,能夠爲你節省大量的性能。
watch不少人都在用,可是這watch中的這兩個選項deep、immediate,或許不是不少人都知道,我猜。
在選項參數中指定 deep: true,能夠監聽對象中屬性的變化。
在選項參數中指定 immediate: true
, 將當即以表達式的當前值觸發回調,也就是當即觸發一次。
watch: {
obj: {
handler(val, oldVal) {
console.log('屬性發生變化觸發這個回調',val, oldVal);
},
deep: true // 監聽這個對象中的每個屬性變化
},
step: { // 屬性
//watch
handler(val, oldVal) {
console.log("默認當即觸發一次", val, oldVal);
},
immediate: true // 默認當即觸發一次
},
},
複製代碼
箭頭函數綁定了父級做用域的上下文,this 將不會按照指望指向 Vue 實例。 也就是說,你不能使用this來訪問你組件中的data數據以及method方法了。 this將會指向undefined。
// 我所採用的方法,我的感受比較簡潔一些,少了一步引入賦值。
const router = new VueRouter({
routes: [
path: '/app',
component: () => import('./app'), // 引入組件
]
})
// Vue路由文檔的寫法:
const app = () => import('./app.vue') // 引入組件
const router = new VueRouter({
routes: [
{ path: '/app', component: app }
]
})
複製代碼
若是咱們的路由比較多的話,是否是要在路由上方引入十幾行組件?
第一種跟第二種方法相比就是把引入賦值的一步,直接寫在component上面,本質上是同樣的。兩種方式均可以的,只是第一種是懶加載的方式。
export default new Router({
routes: [
{
path: '/', // 項目啓動頁
redirect:'/login' // 重定向到下方聲明的路由
},
{
path: '*', // 404 頁面
component: () => import('./notFind') // 或者使用component也能夠的
},
]
})
複製代碼