vue的一些注意点
Vue注意点
v-html
你的站点上动态渲染的任意 HTML 可能会非常危险,因为它很容易导致 XSS 攻击。请只对可信内容使用 HTML 插值,绝不要对用户提供的内容插值。
利用标签模板,过滤 HTML 字符串,防止用户输入恶意内容。
function SaferHTML(templateData) {
let s = templateData[0];
console.log(s);
for (let i = 1; i < arguments.length; i++) {
let arg = String(arguments[i]);
// Escape special characters in the substitution.
s += arg.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">");
// Don't escape special characters in the template.
s += templateData[i];
}
return s;
}
条件渲染
template不支持v-show
默认情况下在切换dom时相同的结构会被复用,如果不需要复用,需要添加key
<template v-if="loginType === 'username'">
<label>Username</label>
<input placeholder="Enter your username" key="1">
</template>
<template v-else>
<label>Email</label>
<input placeholder="Enter your email address" key="2">
</template>
checkbox
多个复选框。v-model需要绑定到一个数组
//html
<div id='example-3'>
<input type="checkbox" id="jack" value="Jack" v-model="checkedNames">
<label for="jack">Jack</label>
<input type="checkbox" id="john" value="John" v-model="checkedNames">
<label for="john">John</label>
<input type="checkbox" id="mike" value="Mike" v-model="checkedNames">
<label for="mike">Mike</label>
<br>
<span>Checked names: {{ checkedNames }}</span>
</div>
//js
new Vue({
el: '#example-3',
data: {
checkedNames: []
}
})
多选时的select框同理
箭头函数
不要在选项属性或回调上使用箭头函数,比如 created: () => console.log(this.a) 或
vm.$watch('a', newValue => this.myMethod())。因为箭头函数是和父级上下文绑定在一起的,this 不会是如你所预期的 Vue 实例,经常导致 Uncaught TypeError: Cannot read property of undefined 或
Uncaught TypeError: this.myMethod is not a function 之类的错误。
数组更新检测
Vue 包含一组观察数组的变异方法,所以它们也将会触发视图更新。这些方法如下:push()、pop()、shift()、unshift()、splice()、sort()、reverse()。
变异方法 (mutation method),顾名思义,会改变被这些方法调用的原始数组。相比之下,也有非变异 (non-mutating method) 方法,例如:filter(), concat() 和 slice() 。这些不会改变原始数组,但总是返回一个新数组。当使用非变异方法时,可以用新数组替换旧数组:
example1.items = example1.items.filter(function (item) {
return item.message.match(/Foo/)
})
注意事项
由于 JavaScript 的限制,Vue 不能检测以下变动的数组:
var vm = new Vue({
data: {
items: ['a', 'b', 'c']
}
})
vm.items[1] = 'x' // 不是响应性的
vm.items.length = 2 // 不是响应性的
为了解决第一类问题,以下两种方式都可以实现和 vm.items[indexOfItem] = newValue 相同的效果,同时也将触发状态更新:
// Vue.set
Vue.set(vm.items, indexOfItem, newValue)
// Array.prototype.splice
vm.items.splice(indexOfItem, 1, newValue)
你也可以使用 vm.$set 实例方法,该方法是全局方法 Vue.set 的一个别名:
vm.$set(vm.items, indexOfItem, newValue)
为了解决第二类问题,你可以使用 splice:
vm.items.splice(newLength)
Vue.set可改为vm.$set
对象更改检测注意事项
还是由于 JavaScript 的限制,Vue 不能检测对象属性的添加或删除:
var vm = new Vue({
data: {
a: 1
}
})
// `vm.a` 现在是响应式的
vm.b = 2
// `vm.b` 不是响应式的
对于已经创建的实例,Vue 不能动态添加根级别的响应式属性。但是,可以使用 Vue.set(object, key, value) 方法向嵌套对象添加响应式属性。例如,对于:
var vm = new Vue({
data: {
userProfile: {
name: 'Anika'
}
}
})
你可以添加一个新的 age 属性到嵌套的 userProfile 对象:
Vue.set(vm.userProfile, 'age', 27)
如果需要为已有对象赋予多个属性:
vm.userProfile = Object.assign({}, vm.userProfile, {
age: 27,
favoriteColor: 'Vue Green'
})
删除属性用Vue.delete或者vm.$delete。
Vue.delete(vm.items,0);
//或者
vm.$delete(vm.items,0);
解析 DOM 模板时的注意事项
有些 HTML 元素,诸如 <ul>、<ol>、<table> 和 <select>,对于哪些元素可以出现在其内部是有严格限制的。而有些元素,诸如 <li>、<tr> 和 <option>,只能出现在其它某些特定的元素内部。
这会导致我们使用这些有约束条件的元素时遇到一些问题。例如:
<table>
<blog-post-row></blog-post-row>
</table>
这个自定义组件
<table>
<tr is="blog-post-row"></tr>
</table>
需要注意的是如果我们从以下来源使用模板的话,这条限制是不存在的:
- 字符串 (例如:template: '...')
- 单文件组件 (.vue)
- x-template
单个根元素
每个组件必须只有一个根元素
<h3>{{ title }}</h3>
<div v-html="content"></div>
以上模板会报错,将其包裹在一个父元素内即可解决
<div class="blog-post">
<h3>{{ title }}</h3>
<div v-html="content"></div>
</div>
组件名大小写
当组件名使用 kebab-case方式时,在引用这个自定义元素时必须使用 kebab-case
当组件名使用 PascalCase方式时,引用这个自定义元素时可使用 kebab-case 或者 PascalCase
但是,直接在 DOM (即非字符串的模板) 中使用时只有 kebab-case 是有效的
事件名
跟组件和 prop 不同,事件名不存在任何自动化的大小写转换。而是触发的事件名需要完全匹配监听这个事件所用的名称。
this.$emit('myEvent')
<my-component v-on:my-event="doSomething"></my-component>
以上监听是无法生效的,v-on 事件监听器在 DOM 模板中会被自动转换为全小写 (因为 HTML 是大小写不敏感的)。因此,推荐始终使用 kebab-case 的事件名
事件处理
常规绑定方法
//html
<div id="example-2">
<!-- `greet` 是在下面定义的方法名 -->
<button v-on:click="greet">Greet</button>
</div>
//js
var example2 = new Vue({
el: '#example-2',
data: {
name: 'Vue.js'
},
// 在 `methods` 对象中定义方法
methods: {
greet: function (event) {
// `this` 在方法里指向当前 Vue 实例
alert('Hello ' + this.name + '!')
// `event` 是原生 DOM 事件
if (event) {
alert(event.target.tagName)
}
}
}
})
内联 JavaScript 语句中调用方法
//html
<div id="example-3">
<button v-on:click="say('hi', $event)">Say hi</button>
<button v-on:click="say('what', $event)">Say what</button>
</div>
//js
new Vue({
el: '#example-3',
methods: {
say: function (message, event) {
alert(message)
}
}
})
v-model
<input v-model="searchText">
//等价于
<input
v-bind:value="searchText"
v-on:input="searchText = $event.target.value"
>
给组件添加 v-model 属性时,默认会把 value 作为组件的属性,然后把 'input' 值作为给组件绑定事件时的事件名
但是在单选框、复选框等类型的输入控件,需要的就不是默认的value特性以及input事件了,有两种方法进行修改
html
<base-checkbox v-model="lovingVue"></base-checkbox>
- 修改v-model语法糖
Vue.component('base-checkbox', {
props:['value']
template: `
<input
type="checkbox"
v-bind:checked="value"
v-on:change="$emit('change', $event.target.checked)"
>
`
})
- 在 Vue 2.2 版本,你可以在定义组件时通过 model 选项的方式来定制 prop/event:
Vue.component('base-checkbox', {
model: {
prop: 'checked',
event: 'change'
},
props: {
checked: Boolean
},
template: `
<input
type="checkbox"
v-bind:checked="checked"
v-on:change="$emit('change', $event.target.checked)"
>
`
})
为组件添加原生事件
为v-on添加.native修饰符即可
<base-input v-on:focus.native="onFocus"></base-input>
特殊情况: 如果使用focus监听input元素,并且input元素被包裹在一个父元素中,.native监听器将默认失败
$attrs、$listeners、inheritAttrs
名词解释
$attrs--继承所有的父组件属性(除了prop传递的属性、class 和 style )
inheritAttrs:默认值true,继承所有的父组件属性(除props的特定绑定)作为普通的HTML特性应用在子组件的根元素上,如果你不希望组件的根元素继承特性设置inheritAttrs: false,但是class属性和style会继承。
$listeners--属性,它是一个对象,里面包含了作用在这个组件上的所有监听器,你就可以配合 v-on="$listeners" 将所有的事件监听器指向这个组件的某个特定的子元素。
代码演示:
//HTML
<div id="app">
<base-input v-model="a" placeholder="tip" label="name" class="a" :style="{color: 'red'}"></base-input>
</div>
//JS
Vue.component('base-input', {
props: ['value'],
template: `
<label>
{{$attrs.placeholder}}
{{$attrs.label}}
<input
v-bind:value="value"
v-on:input="$emit('input', $event.target.value)"
v-bind="$attrs">
</label>
`
});
var v = new Vue({
el: '#app',
data: {
a: '123',
}
});
因为inheritAttrs的默认值为ture,所以组件的根元素label将继承父组件的所有属性(除了class、style和props传递的属性);组件内的input则通过v-on="$attrs"绑定了父组件的属性。
渲染结果为:
<label placeholder="tip" label="name" class="a" style="color: red;">
tip
name
<input placeholder="tip" label="name">
</label>
若将inheriAttrs值设为false,
根元素label将不会继承父组件的属性。
<label class="a" style="color: red;">
tip
name
<input placeholder="tip" label="name">
</label>
$attrs可以打包父组件的属性,同样的,$listeners则打包父组件的事件
//html
<div id="app">
<base-input @click="func" @focus="funf" v-model="a" placeholder="tip" label="name" class="a" :style="{color: 'red'}"></base-input>
</div>
//js
Vue.component('base-input', {
// inheritAttrs: false,
props: ['value'],
template: `
<label>
{{$attrs.placeholder}}
{{$attrs.label}}
<input
v-bind:value="value"
v-on:input="$emit('input', $event.target.value)"
v-bind="$attrs"
v-on="$listeners">
</label>
`
});
var v = new Vue({
el: '#app',
data: {
a: '123',
},
methods: {
func: function(){
console.log('被点击了');
},
funf: function(){
console.log('获得焦点');
}
},
});
插槽
如果组件没有包含一个
$nextTick()
如果数据变化后想获取真实dom中的内容,需要等待页面渲染完毕后再去获取所有的dom操作最好放在nextTick中
slot插槽
如果组件模板没有包含一个
<my-component>
Your Profile
</my-component>
当有多个内容需要分配至相应插槽中,可以将内容包裹在设置了slot特性的中
如果 上面代码中,h1会被保存在name为header的slot中,而p、a标签将会被保存在默认插槽中。 插槽标签内部加入内容可以起到默认值效果, 如果父组件为这个插槽提供了内容,则默认的内容会被替换掉。 假设我们的todo-list组件被多个地方调用,而我们希望能在不同的地方调用组件的时候,组件的todo.text不仅限于以li的形式渲染,而是能以自定义的形式渲染,这时,作用域插槽可以满足: 首先,在todo-list模板中,将li替换为slot,意味着slot具体被渲染成什么将由html中的todo-list组件的内容决定;接下来,在todo-list组件内添加template标签并设置slot-scope属性来接收slot所绑定的todo属性。就此,todo对象传递给了slotProps,todo-list组件内的li可以随意替代为其他标签。 我们之前曾经在一个多标签的界面中使用 is 特性来切换不同的组件: 当在这些组件之间切换的时候,你有时会想保持这些组件的状态,以避免反复重渲染导致的性能问题。这时,我们在外围包裹keep-alive即可//html
<my-component>
<template slot="header">
<h1 slot="header">这是标题</h1>
<p slot="test">这是段落</p>
<a href="baidu.com">百度</a>
</template>
<div>盒子</div>
</my-component>
<template id="my">
<header>
<slot name="header"></slot>
</header>
<slot></slot>
</template>
//js
my-component{
template:'#my'
}
//html
<my-component>
<h1 slot="header">这是标题</h1>
<p slot="test">这是段落</p>
<a href="baidu.com">百度</a>
</my-component>
<template id="my">
<slot name="header"></slot>
<slot></slot>
</template>
//js
my-component{
template:'#my'
}
<button type="submit">
<slot>Submit</slot>
</button>
作用域插槽
//todo-list模板
<ul>
<li
v-for="todo in todos"
v-bind:key="todo.id"
>
{{ todo.text }}
</li>
</ul>
//html
<todo-list :todos="todos">
<!-- 将 `slotProps` 定义为插槽作用域的名字 -->
<template slot-scope="slotProps">
<li>{{slotProps.text}}</li>
</template>
</todo-list>
//js
//todo-list模板
<ul>
<slot v-for="todo in todos" :todo="todo" :key="todo.id">
{{ todo.text }}
</slot>
</ul>
关于
<component :is="currentTabComponent"></component>
<keep-alive>
<component v-bind:is="currentTabComponent"></component>
</keep-alive>