Vue2
# Vue2
# 创建全局组件
Vue.component(name, {
template: '', // html代码
props: ,
})
2
3
4
需要在创建实例之前使用该方法。
# 计算属性
计算属性是基于响应式依赖进行缓存的,相比方法,只有在相关响应式依赖发生变化时才重新求值。
# 原理
src/core/instance/state.js,参考链接 (opens new window)。
initState函数中,initComputed初始化computed。initComputed中- 遍历
computed,获取(key, value),即属性名和计算的方法。 - 对每一个计算属性
- 创建
Watcher实例 - 初始化
getter、deps(依赖哪些属性)、dep(发布者,以备未来有被订阅) defineComputed--- 定义set、get(createComputedGetter)计算方法,然后通过Object.defineProperty()设置vue实例的属性的set、get方法。
- 创建
- 遍历
针对
createComputedGetterwatcher.evaluate()如果已创建的
Watcher实例,是计算方法,执行evaluate()获取值。evaluate本质是调用之前定义的Watcher实例的getter方法。如果要求是depp深度监听,将重新收集所有依赖。watcher.depend():如果存在Dep.target,收集依赖。
当对一个对象使用
getters时,同样会调用其子属性的getters。这样每一个属性对应的watcher都会被推入Dep类的静态属性target,从此每一个属性都将被收集到计算属性的依赖。
# key
Vue 会尽可能高效地渲染元素,通常会复用已有元素而不是从头开始渲染。
Vue 为你提供了一种方式来表达“这两个元素是完全独立的,不要复用它们”。只需添加一个具有唯一值的 key attribute 即可。
# v-for
永远不要把
v-if和v-for同时用在同一个元素上。v-for的优先级比v-if的优先级更高,因此每次重渲染时,都会遍历整个列表,不论活跃用户是否发生了变化,因此浪费性能。解决方法:
- 先使用计算属性筛选一次。
- 将
v-if转移到容器元素上。
key不能使用index不要使用对象或数组之类的非基本类型值作为
v-for的key。请用字符串或数值类型的值。index代表当前项的索引(0,1,2,3,4,...),当发生删除、增加等操作时,其后面的元素的index都会发生变化,此时diff算法就认为后面的key-index映射全部发生了变化,将全部重新渲染,严重影响性能。因此推荐key使用唯一值,如时间戳+new Date()、身份证号、学号等...
# 同步组件和异步组件
# 同步组件
import componentA from './componentA.vue'
# 异步组件
只在组件需要渲染的时候才进行加载渲染并进行缓存,以备下次访问。
componentA: () => import('./componentA.vue')
调用异步组件的方法-延时
setTimeout(() => {
this.$nextTick(() => {
console.log(this.$refs.com);
});
}, 100);
2
3
4
5
优点:提升首页渲染速度。
# 区别
nextTick
父组件获取子组件时:
同步组件:nextTick可以获取组件。
异步组件:第一次nextTick之后无法获取组件。
打包
打包成单独的js文件存储在static/js文件夹里面
生命周期顺序
- 引入时:
异步组件:父组件
beforeCreate、created、beforeMount、mounted--->挨个子组件beforeCreate、created、beforeMount、mounted同步组件:父组件
beforeCreate、created、beforeMount--->挨个子组件beforeCreate、created、beforeMount--->挨个子组件mounted---> 父组件mounted- remove时 异步组件和同步组件一致,父组件beforeDestroy ---> 子组件beforeDestroy、destroyed ---> 父组件destroyed
# 组件
当一个组件被定义,
data必须声明为返回一个初始数据对象的函数,因为组件可能被用于创建多个实例。否则,会出现多个组件使用一个数据对象的情况。
使用事件抛出值
子组件
<button @click="$emit('put', 999)"><button />1父组件
num接收来自子组件传来的值num<Father @put="num = $event"/>1或者
<Father @put="change"/> methods:{ change(num) { // change方法的第一个形参即子组件传来的值 } }1
2
3
4
5
6
7
8
组件使用
v-model父组件
// 法一:有缺陷,初始值传不过去 <Father v-model="message" /> // 法二:刨根问底 <Father v-bind:message="message" v-on:input="message = $event"> // or <Father :message="message" @input="message = $event">1
2
3
4
5
6子组件
Vue.component('Son', { props: ['message'], template: ` <input v-bind:value="message" v-on:input="$emit('input', $event.target.value)" > ` })1
2
3
4
5
6
7
8
9
小知识:
update:myPropName模式可以用.sync修饰符使父子组件通信的prop进行双向绑定。子组件
数据发生变化时
this.$emit('update:title', newtitle)1父组件
刨根问底
<text-document v-bind:title="doc.title" v-on:update:title="doc.title = $event" ></text-document>1
2
3
4.sync修饰符<text-document v-bind:title.sync="doc.title"></text-document>1
# prop
子组件接收
prop并作为本地数据使用。props: ['initCount'], data() { return { count: this.initCount } }1
2
3
4
5
6带有默认值的对象
对象或数组默认值必须从一个工厂函数获取
props: { propA: { type: Object, // 对象或数组默认值必须从一个工厂函数获取 default: function () { return { message: 'hello' } } } }1
2
3
4
5
6
7
8
9
包含了父作用域中不作为 prop 被识别 (且获取) 的 attribute 绑定 (class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind="$attrs" 传入内部组件——在创建高级别的组件时非常有用。
注意:当创建高阶组件时,可用$attrs获取父作用域的props。
# 插槽
# 使用
a.vue
<template> <div> <header> <slot name="header"></slot> </header> <main> <slot></slot> </main> <footer> <slot name="footer"></slot> </footer> </div> </template>1
2
3
4
5
6
7
8
9
10
11
12
13App.vue
<A> <template v-slot:header> <h1>header</h1> </template> <template v-slot:footer> <h1>footer</h1> </template> <h1>main</h1> </A>1
2
3
4
5
6
7
8
9
# 插槽内容使用子组件数据
子组件
<slot v-bind:propName="propName"></slot>
// propName是子组件内部数据
2
插槽内容
<template v-slot="allProp">
// allProp可以使用所有插槽的所有属性,通过打点区分。
</template>
2
3
# 注意事项
- 不带
name的<slot>默认名称为“default”。 v-slot只能添加在<template>上。
# 访问组件或元素
# 访问根实例
this.$root
# 访问父组件
this.$parent
# 访问子组件
this.$childrenref
# 依赖注入
提示:provide 和 inject 绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的 property 还是可响应的。
父组件使用实例新选项provide
provide() {
return {
getMap: this.getMap
}
}
2
3
4
5
子组件使用实例新选项inject
inject: ['getMap']
# vue生命周期
new Vue()初始化事件和生命周期。
breforeCreate()创建实例前,可以访问实例选项
$options,但无法访问$el、data等。created()创建实例后,可以访问
data,无法访问$el。created~breforeMount查询是否有
$el选项,没有则暂停生命周期,需手动挂载$mount,反之进行下一步。查询是否有模板
template,没有则使用外部HTML作为模板编译,反之则render函数编译模板。优先级:
render>template>outHTML
beforeMount()挂载前,无法访问
$el。mounted()挂载后,可以访问
$el。breforeUpdate()视图层
view数据并未更新,$el、data均已更新,但真实DOM还未更新。updated()视图层
view数据更新,真实DOM更新。beforeDestroy()仍可使用
this、destoryed()Vue实例销毁,解绑事件监听、子实例、
watcher。
# vue自定义指令
# 全局注册
Vue.directive('focus', {
inserted: function(el) {
el.focus()
}
})
2
3
4
5
# 局部注册
实例新选项directives
directives: {
focus: {
inserted: function (el) {
el.focus()
}
}
}
2
3
4
5
6
7
使用:v-focus
# forceUpdate
迫使 Vue 实例重新渲染。注意它仅仅影响实例本身和插入插槽内容的子组件,而不是所有子组件。
# 过滤器
常在双花括号插值、v-bind表达式中用于文本格式化,需被添加在JavaScript表达式尾部。
如:
<!-- 在双花括号中 -->
{{ message | capitalize }}
<!-- 在 `v-bind` 中 -->
<div v-bind:id="rawId | formatId"></div>
2
3
4
5
# 创建过滤器
局部
filters: { function a(){} }1
2
3全局
Vue.filter(funcName, function (value) {})1
# 使用
中message用于函数filterA的第一个参数,filterA的结果被传入filterB。
# 混入
创建混入对象
const myMixin = { created() { this.show() }, methods:{ show() { console.log('混入!') } }, }1
2
3
4
5
6
7
8
9
10使用混入对象
const vm = new Vue({ mixins: [myMixin] }) // 全局引入: Vue.mixin(myMixin)1
2
3
4
5
注意事项:
- 选项合并发生冲突时,以组件数据为准。未冲突,将合并。
- 钩子函数合并,混入先被调用,其次调用组件的。
# 插件
# 开发插件
插件暴露install(Vue, options)方法,在此方法里do something。
const obj = {
install(_Vue) {
// 如果较多时,可以forEach批量注册components
_Vue.component("my", {
render(createElement) {
return createElement("h" + this.level, this.$slots.default);
},
props: {
level: {
type: Number,
require: true,
},
},
});
},
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 使用插件
Vue.use(obj)
# 过渡
# 单元素/组件过渡
Vue提供了封装组件transition
<transition name=""></transition>
常用于:
v-ifv-show- 动态组件
- 组件根节点
# 过渡类名
默认无名transition:v-enter、v-enter-active、v-enter-to、v-leave、v-leave-active、v-leave-to。
有name:name取代v。
# 自定义类名
enter-classenter-active-classenter-to-class(2.1.8+)leave-classleave-active-classleave-to-class(2.1.8+)
# 注意事项
- CSS 过渡在动画中
v-enter类名在节点插入 DOM 后不会立即删除,而是在animationend事件触发时删除。
# 多元素过渡
可使用v-if/v-else。
# 过渡模式
transition新props:mode
mode常用值:
out-in:当前元素过渡完,新元素再来。in-out:新元素先过渡,当前元素再走。
# 注意事项
- 同名标签切换,记得设置
key。
# 多组件过渡
可使用动态组件。
# 列表过渡
<transition-group>
常用props:
tag:默认span标签。<transition-group>为真实元素,默认span。
# 可复用过渡组件
使<transition>或<transition-group>成为根组件。
# 动态过渡
所有属性都是动态可绑定的。
# 模板template编译原理
流程
定位模板
寻找根节点
$el- 不存在,手动
mount,下一步。 - 存在,下一步。
寻找模板
template,render函数编译。- 不存在,使用外部HTML。
- 存在,使用。
- 不存在,手动
解析器:生成AST语法树
一段一段生成,开始标签、文本、注释、结束标签。确定层级关系使用栈,开始标签推入栈,结束标签弹出栈。
优化器:标记静态节点
方便重新渲染,不需再为静态节点创建新虚拟树。
标记:
- 所有静态子树。
- 静态根树。
代码生成器:生成render函数
首先,得到渲染函数字符串。
with (this) { return _c( 'div', { attrs: {"id": "app"} }, [ _c( 'div', { staticClass: "class-name", attrs: { "title": `title`} }, [ _v(" "+_s(name)+" ") ] ), _v(" "), _c( 'div', [_v("tetttt")] ) ] ) }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20再通过new Function得到渲染函数,以便得到该模板的虚拟DOM。
注意:
_c:createElement,处理元素节点为虚拟DOM节点。_v:createTextVNode,处理文本节点为虚拟DOM节点。_e:createEmptyVnode,处理注释节点为虚拟DOM节点。
# 数组变化监听
push()pop()shift()unshift()splice()sort()reverse()