<template>
<div>
<my-component v-model="value"></my-component>
<!-- 等同 -->
<my-component :value="value" @input="value=$event"></my-component>
<button @click="value=true">显示</button>
</div>
</template>
<script>
export default{
data(){
return{
value:false,
}
},
components:{
myComponent:resolve =>require(['./my_component'],resolve),
}
}
</script>
复制代码
<template>
<div v-show="value">
<span>个人组件</span>
<button @click="$emit('input',false)">隐藏</button>
</div>
</template>
<script>
export default{
props:{
value:{
type:Boolean,
default:false,
}
},
data(){
return{}
},
}
</script>
复制代码
其有三个参数css
include
定义缓存白名单,会缓存的组件;exclude
定义缓存黑名单,不会缓存的组件;include="a,b"
、:include="/a|b/"
、:include="['a', 'b']"
;max
最多能够缓存多少组件实例。一旦这个数字达到了,在新实例被建立以前,已缓存组件中最久没有被访问的实例会被销毁掉;当它们处于同一节点,v-for
的优先级比v-if
更高,这意味着v-if
将分别重复运行于每一个v-for
循环中。当你只想为部分项渲染节点时,这种优先级的机制会十分有用。html
<ul>
<li v-for="item in items" v-if="item.show">{{item}}</li>
</ul>
复制代码
若是你的目的是有条件地跳过循环的执行,那么能够将 v-if 置于外层元素 (或<template>
)上。vue
<ul v-if="items.length">
<li v-for="item in items">{{item}}</li>
</ul>
复制代码
主要看v-for渲染的是什么。node
<template>
<div>
<span v-for="item in lists">{{item}}</span>
</div>
</template>
<script>
export default {
data() {
return {
lists: [1, 2, 3, 4, 5]
}
},
}
</script>
复制代码
以上的例子,v-for的内容会生成如下的DOM节点数组,咱们给每个节点标记一个身份id,以辨别节点的位置:[
'<span>1</span>', // id: A
'<span>2</span>', // id: B
'<span>3</span>', // id: C
'<span>4</span>', // id: D
'<span>5</span>' // id: E
]
复制代码
将lists中的数据进行位置调换,变成[2,4,3,1,5]
,在没有key的情景下,节点位置不变,可是节点的内容更新了,这就是“就地更新”[
'<span>2</span>', // id: A
'<span>4</span>', // id: B
'<span>3</span>', // id: C
'<span>1</span>', // id: D
'<span>5</span>' // id: E
]
复制代码
可是在有key的情景下,节点位置进行了交换,可是内容没有更新[
'<span>2</span>', // id: B
'<span>4</span>', // id: D
'<span>3</span>', // id: C
'<span>1</span>', // id: A
'<span>5</span>' // id: E
]
复制代码
// vue源码 src/core/vdom/patch.js 488行
// 如下是为了阅读性进行格式化后的代码
// oldCh 是一个旧虚拟节点数组
// oldKeyToIdx map映射对象
// idxInOld 对比后获得旧节点下标
if (isUndef(oldKeyToIdx)) {
oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx)
}
if (isDef(newStartVnode.key)) {
// map 方式获取
idxInOld = oldKeyToIdx[newStartVnode.key]
} else {
// 遍历方式获取
idxInOld = findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx)
}
复制代码
建立map函数function createKeyToOldIdx(children, beginIdx, endIdx) {
let i, key
const map = {}
for (i = beginIdx; i <= endIdx; ++i) {
key = children[i].key
if (isDef(key)) map[key] = i
}
return map
}
复制代码
遍历寻找函数// sameVnode 是对比新旧节点是否相同的函数
function findIdxInOld(node, oldCh, start, end) {
for (let i = start; i < end; i++) {
const c = oldCh[i];
if (isDef(c) && sameVnode(node, c)) return i
}
}
复制代码
还能够强制替换元素/组件而不是重复使用它。在如下场景可使用正则表达式
<transition>
<span :key="text">{{ text }}</span>
</transition>
复制代码
当 text 发生改变时,<span>
会随时被更新,所以会触发过渡。算法
不要使用对象或数组之类的非基本类型值做为key,请用字符串或数值类型的值;npm
不要使用数组的index做为key值,由于在删除数组某一项,index也会随之变化,致使key变化,渲染会出错。json
例:在渲染[a,b,c]
用 index 做为 key,那么在删除第二项的时候,index 就会从 0 1 2 变成 0 1(而不是 0 2),随之第三项的key变成1了,就会误把第三项删除了。后端
给组件命名有两种方式,一种是使用链式命名my-component,一种是使用大驼峰命名MyComponent,数组
在字符串模板中<my-component></my-component>
和 <MyComponent></MyComponent>
均可以使用,
在非字符串模板中最好使用<MyComponent></MyComponent>
,由于要遵循W3C规范中的自定义组件名 (字母全小写且必须包含一个连字符),避免和当前以及将来的 HTML 元素相冲突。
<myComponent@diy="handleDiy"></myComponent>
,在子组件用this.$emit('diy',data)
来触发这个diy事件,其中data为子组件向父组件通讯的数据,在父组件中监听diy个事件时,能够经过$event访问data这个值。.sync
绑定一个数据<myComponent :show.sync="show"></myComponent>
,在子组件用this.$emit('updata:show',data)
来改变父组件中show
的值。v-model
。is
特殊特性和component
内置组件标签时使用;keep-alive
内置组件标签中include
和exclude
属性中使用。递归引用能够理解为组件调用自身,在开发多级菜单组件时就会用到,调用前要先设置组件的name选项, 注意必定要配合v-if使用,避免造成死循环,用element-vue组件库中NavMenu导航菜单组件开发多级菜单为例:
<template>
<el-submenu :index="menu.id" popper-class="layout-sider-submenu" :key="menu.id">
<template slot="title">
<Icon :type="menu.icon" v-if="menu.icon"/>
<span>{{menu.title}}</span>
</template>
<template v-for="(child,i) in menu.menus">
<side-menu-item v-if="Array.isArray(child.menus) && child.menus.length" :menu="child"></side-menu-item>
<el-menu-item :index="child.id" :key="child.id" v-else>
<Icon :type="child.icon" v-if="child.icon"/>
<span>{{child.title}}</span>
</el-menu-item>
</template>
</el-submenu>
</template>
<script>
export default{
name: 'sideMenuItem',
props: {
menu: {
type: Object,
default(){
return {};
}
}
}
}
</script>
复制代码
$attrs
和$listeners
的使用场景?$attrs
: 包含了父做用域中(组件标签)不做为 prop 被识别 (且获取) 的特性绑定 (class 和 style 除外)。 在建立基础组件时候常用,能够和组件选项inheritAttrs:false
和配合使用在组件内部标签上用v-bind="$attrs"
将非prop特性绑定上去;$listeners
: 包含了父做用域中(组件标签)的 (不含.native
) v-on 事件监听器。 在组件上监听一些特定的事件,好比focus事件时,若是组件的根元素不是表单元素的,则监听不到,那么能够用v-on="$listeners"
绑定到表单元素标签上解决。在有使用$on
的组件中要在beforeDestroy
钩子函数中用$off
销毁。
要,否则会形成屡次绑定和内存泄露。关于移除事件监听的坑。
data
选项中建立一个对象timer
,给每一个定时器取个名字一一映射在对象timer
中, 在beforeDestroy
构造函数中for(let k in this.timer){clearInterval(k)}
;const timer = setInterval(() =>{}, 500);
this.$once('hook:beforeDestroy', () => {
clearInterval(timer);
})
复制代码
push()
、pop()
、shift()
、unshift()
、splice()
、sort()
、reverse()
,这些方法在Vue中被从新定义了,故能够监听到数组变化;filter()
、concat()
、slice()
,这些方法会返回一个新数组,也能够监听到数组的变化。利用索引直接设置一个数组项时;
修改数组的长度时。
Object.defineProperty()
是能够监听到,利用不存在的索引直接设置一个数组项时Object.defineProperty()
是不能够监听到,可是官方给出的解释是因为JavaScript的限制,Vue不能检测以上数组的变更,其实根本缘由是性能问题,性能代价和得到的用户体验收益不成正比。Object.defineProperty()
不能监听到数组的length
属性。用this.$set(this.items, indexOfItem, newValue)
或this.items.splice(indexOfItem, 1, newValue)
来解决第一种状况;
用this.items.splice(newLength)
来解决第二种状况。
由于Vue是经过Object.defineProperty
来将对象的key转成getter/setter的形式来追踪变化,但getter/setter只能追踪一个数据是否被修改,没法追踪新增属性和删除属性,因此才会致使上面对象变化没法监听。
this.$set(this.obj,"key","newValue")
来解决第一种状况;Object.assign
来解决第二种状况。' '
或undefined
,其余元素键值不变;watch
:一个数据影响多个数据,当须要在数据变化时执行异步或开销较大的操做时;有三种
挂载在Vue的prototype上
// base.js
const install = function (Vue, opts) {
Vue.prototype.demo = function () {
console.log('我已经在Vue原型链上')
}
}
export default {
install
}
复制代码
//main.js
//注册全局函数
import base from 'service/base';
Vue.use(base);
复制代码
利用全局混入mixin
用this.$root.$on
绑定方法,用this.$root.$off
解绑方法,用this.$root.$emit
全局调用。
this.$root.$on('demo',function(){
console.log('test');
})
this.$root.$emit('demo');
this.$root.$off('demo');
复制代码
el
:提供一个在页面上已存在的DOM元素做为Vue实例的挂载目标。能够是CSS选择器,也能够是一个HTMLElement实例。
const vm = new Vue({})
中存在这个选项,实例将当即进入编译过程,不然,须要显式调用vm.$mount()
手动开启编译。<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
</head>
<body>
<div id="app">我是el挂载的内容:小明今年{{age}}岁了</div>
</body>
<script>
const vm= new Vue({
el:'#app',
data:{
age:17
},
}
</script>
</html>
复制代码
<script>
const vm= new Vue({
data:{
age:17
},
})
vm.$mount('#app')
</script>
复制代码
template
:一个字符串模板做为Vue实例的标识使用。若是el
存在,模板将会替换挂载的元素。挂载元素的内容都将被忽略,除非模板的内容有分发插槽。
<script>
const vm= new Vue({
el:'#app',
data:{
age:17
},
template:'<div>我是template的内容:小明今年{{age}}岁了</div>',
})
</script>
复制代码
<script type="x-template" id="mb">
<div>我是template的内容:小明今年{{age}}岁了</div>
</script>
<script>
const vm= new Vue({
el:'#app',
data:{
age:17
},
template:'#mb',
})
</script>
复制代码
<body>
<div id="app">
我是el挂载的内容:小明今年{{age}}岁了
</div>
<template id="mb">
<div>我是template的内容:小明今年{{age}}岁了</div>
</template>
</body>
<script>
const vm= new Vue({
el:'#app',
data:{
age:17
},
template:'#mb',
})
</script>
复制代码
render
:Vue 选项中的 render 函数若存在,则 Vue 构造函数不会从 template 选项或经过 el 选项指定的挂载元素中提取出的 HTML 模板编译渲染函数。<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
</head>
<body>
<div id="app">
我是el挂载的内容:小明今年{{age}}岁了
</div>
</body>
<script>
const vm= new Vue({
el:'#app',
data:{
age:17
},
template:'<div>我是template的内容:小明今年{{age}}岁了</div>',
render(h){
return h('div',`我是render的内容:小明今年${this.age}岁了`)
}
})
</script>
</html>
复制代码
<template></template>
有什么用?用delimiters
选项,其默认是["{{", "}}"]
// 将分隔符变成ES6模板字符串的风格
new Vue({
delimiters: ['${', '}']
})
复制代码
以 _
或 $
开头的属性 不会 被 Vue 实例代理,由于它们可能和 Vue 内置的属性、API 方法冲突,你可使用例如 vm.$data._property
的方式访问这些属性。
errorCaptured
是组件内部钩子,当捕获一个来自子孙组件的错误时被调用,接收error
、vm
、info
三个参数,return false
后能够阻止错误继续向上抛出。errorHandler
为全局钩子,使用Vue.config.errorHandler
配置,接收参数与errorCaptured
一致,2.6后可捕捉v-on
与promise
链的错误,可用于统一错误处理与错误兜底。<link rel="icon" href="<%= BASE_URL %>favicon.ico">
, 其中<%= BASE_URL %>
等同vue.config.js中publicPath
的配置;<link rel="icon" type="image/png" href="">
import browserImg from 'images/kong.png';//为favicon的默认图片
const imgurl ='后端传回来的favicon.ico的线上地址'
let link = document.querySelector('link[type="image/png"]');
if (imgurl) {
link.setAttribute('href', imgurl);
} else {
link.setAttribute('href', browserImg);
}
复制代码
build.assetsPublicPath
的值;在项目中通常经过配置alias路径别名的方式解决,下面是Vue CLI3的配置。
configureWebpack: {
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'@': resolve('src'),
'assets': resolve('src/assets'),
'css': resolve('src/assets/css'),
'images': resolve('src/assets/images'),
}
},
},
复制代码
由于动态添加src被当作静态资源处理了,没有进行编译,因此要加上require。
<template>
<img class="logo" :src="logo" alt="公司logo">
</template>
<script>
export default {
data() {
return {
logo:require("assets/images/logo.png"),
};
}
};
</script>
复制代码