- Published on
vue 基本概念
- Authors
- Name
- jacob jiang
vuejs [2.X] 的理解
vue 为 js构建用户界面的库,通过编译模板生成【少部分用jsx】生成(最终都是调用createElement函数)虚拟dom ,通过改变虚拟dom【相当于给了一个缓冲层】, 来整体更新web 界面【将许多dom 操作在某个时刻来更新渲染,从而减少重绘,渲染的计算消耗】。
框架特点:响应式数据,数据变动产生副作用,模板语法【html 体验】
副作用包括:
- data 数据改变,会去更新模板视图中的数据。例如:
v-bind
{{}}
- compute 数据会根据data 【组件自生响应式状态】
模板语法
- 声明式渲染,数据绑定
- 条件渲染
💁♂️ v-show 不管如何都会调用render 函数生成虚拟节点,并且是基于css 。然而v-if 是惰性的【指不会去生成$el 对象】。
- 列表渲染
💁♂️ v-if 与 v-for 一起使用不能放在同一个 标签上,看是为了渲染过滤后元素,还是为了隐藏列表。
列表的key 是为了尽可能的最新代价更新。
- 事件绑定
- 数据双向绑定
指令: [参数].修饰符 = value
[参数] 为动态参数(使用变量代替)
vue object
创建一个vue 实例时,传入一个options 对象。 常见的选项:
vue应用, 是许多vue实例以树形结构连接起来,与DOM 树类似。vue 应用挂载根元素(#app)实例,在mounted 阶段通过render 函数返回的createNodeDescription 的对象,进行渲染DOM节点。
当createNodeDescription
中的data数据 发生改变时,就会触发副作用 。触发更新阶段,会去比较新老节点对象,以最小代价更新dom 树。【例如:声明式绑定的数据的更新视图,其他computed 数据的变化】
template
🌟 注意 options 中往往有tempalte属性,.
而在项目中使用*.vue 单文件(Single-File Components)开发组件 template ,是由于vue-loader 的存在,结合webpack可以得到更好的开发体验,而不用写许多丑陋options 对象。
data
注意:data 中初始对象才是响应式的,而且被object.freeze对象无法被追踪变化。
⚠️ 多次复用组件中,data 需要写成一个函数形式【不能使用箭头函数,this 的指向不能确定】,相当一个工厂函数【用来生成装配不同的对象】。
⭐箭头函数没有prototype ,因此没有 constructor,不能作为构造函数。
⭐ 对于响应式数组的一般方法是被包裹,会触发视图更新。
Computed
该选项提供data 衍生出来的状态,例如 被某个属性筛选的列表的列表
⚠️ 计算属性默认只有get,可以提供set .
当需要在数据变化时执行异步或开销较大的操作时,watch这个声明式方式是最有用的。
methods
定义组件相关的事件,也可以理解为行为,当你点击或者拖拽某个组件,就会触发一些
状态更新,或传递给其他组件数据。这些都可以写进方法里。
组件相关选项
components
1️⃣ 组件全局注册 (注册基组件【常用的,例如btn,field】,也是各个组件库的注册方式)
Vue.component('my-component-name', { /* ... */ })
组件名,选项
工厂函数方式加载组件
Vue.component('async-example', function (resolve, reject) {
setTimeout(function () {
// 向 `resolve` 回调传递组件定义
resolve({
template: '<div>I am async!</div>'
})
}, 1000)
})
2️⃣ 组件局部注册 (SFCs 常用的方式,即单文件通常导出一个对象,放入其父组件的Componets 选项中)
var ComponentA = { /* ... */ }
...
new Vue({
el: '#app',
components: {
'component-a': ComponentA,
'component-b': ComponentB,
'my-component': () => import('./my-async-component')//异步方式
}
})
组件名参考风格指南 — Vue.js (vuejs.org)
Props
props: 字符串数组,对象与类型键值对,或者更高级,往往是父组件传给的数据(data),可是往往被用来初始组件的状态。
个人认为需要尽可能将皮肤(即css 能解决的问题),少写在props 中。使用工具类css 往往写出的组件更加简洁。
然而状态(state)或者 data 选项,表示组件内部的状态。一部分是通过props 初始的,另一部分是组件内部维护的状态。
复用性选项
mixins
数据对象在内部会进行递归合并,并在发生冲突时以组件数据优先。
同名钩子函数将合并为一个数组,因此都将被调用。另外,混入对象的钩子将在组件自身钩子之前调用。
值为对象的选项,例如 methods
、components
和 directives
,将被合并为同一个对象。两个对象键名冲突时,取组件对象的键值对。
directives
directives: {//指令选项
focus: {//指令名
// 指令的定义(g钩子函数定义)
inserted: function (el) {
el.focus()
}
}
}
// 注册
Vue.directive('my-directive', {
bind: function () {},
inserted: function () {},
update: function () {},
componentUpdated: function () {},
unbind: function () {}
})
钩子函数参数 el 渲染出来的dom ,可以直接操作
binding[指令相关所有值]
- name , value ,arg ,modifiers ,expression
- 当让可以使用动态arg
vnode 生成的虚拟节点
oldVnode 【只存在于update和componentUpdated】
可以使用过滤器格式化许多文本
动态组件
<!-- 失活的组件将会被缓存!-->
<keep-alive>
<component v-bind:is="currentTabComponent"></component>
</keep-alive>
函数式组件
组件式函数,只是一个接受一些 prop 的函数。在这样的场景下,我们可以将组件标记为 functional
插槽
编译作用域
<navigation-link url="/profile">
Clicking here will send you to: {{ url }}
<!--
url 作为props 传给该组件,里面可以访问插入slot 组件的作用域的data
这里的 `url` 会是 undefined,因为其 (指该插槽的) 内容是
_传递给_ <navigation-link> 的而不是
在 <navigation-link> 组件*内部*定义的。
-->
</navigation-link>
父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。
向具名插槽提供内容的时候,我们可以在一个 <template>
元素上使用 v-slot
指令,并以 v-slot
的参数的形式提供其名称:
<base-layout>
<template v-slot:header>
<h1>Here might be a page title</h1>
</template>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
<template #footer>
<p>Here's some contact info</p>
</template>
</base-layout>
作用域插槽
为了让 user
在父级的插槽内容中可用,我们可以将 user
作为 <slot>
元素的一个 attribute 绑定上去:
<span>
<slot v-bind:user="user"> //user attribute 被称为插槽 prop
{{ user.lastName }}
</slot>
</span>
然后在template 中改名
<current-user>
<template v-slot:default="slotProps"> //slotProps 为所有插槽 props
的对象
{{ slotProps.user.firstName }}
</template>
</current-user>
可以直接使用es6 结构 v-solt 给的 slotProps
<current-user v-slot="{ user: person }">
{{ person.firstName }}
</current-user>
甚至使用 函数中的默认值,用法
<current-user v-slot="{ user = { firstName: 'Guest' } }">
{{ user.firstName }}
</current-user
也可以使用动态插槽名
<base-layout>
<template v-slot:[dynamicSlotName]>
...
</template>
</base-layout>
生命周期
注意几个重要节点
beforeCreate ,在data observer ,injection 初始前,在event与lifeCycle初始化后被调用。
created ,此时 data 可以被拿到,可以做些副作用【网络请求】
然后:
通过两种方式去挂载,一种是当含有el 参数,反之是当有vm.$mount 调用时(手动)。
会去编译Template :
- 如果有template则变为render 函数
- 反之将outerHtml 编译为template [ 需要compiler-included build 版本],然而要渲染最终还是要编译为render函数。一般不走这条
服务端渲染时期被调用
此时开始渲染在页面上
beforeMount ,render 函数首次被调用,返回虚拟dom --- creating vm.$el and to replace 'el' with it 。在创建文档流。还未挂载不会触发update。
mouted ,可以拿到refs。 mouted 并不会保证所有的子组件都会重绘完成。要使用$nextTick()
💁♂️ 与updated 一样不能保证子组件的渲染完成,可以使用$nextTick
mounted: function () {
this.$nextTick(function () {
// Code that will run only after the
// entire view has been rendered
})
}
当(data)数据更新时会去触发更新阶段,beforeUpate 【手动移除事件监听器】 -->re-render / patch --> updated。
beforeUpdate 可以对数据进行更改,并不会触发updated
update此时可以对新节点进行dom 操作。然而在大多数情况下,你应该避免在此期间更改状态【否则又会触发更新!】。如果要相应状态改变,通常最好使用计算属性或 watcher 取而代之。
beforeDestroy 实例销毁前最后一次hook,可以销毁一些引入的对象[listener, 定时器]
⚠️ 不要再生命周期上使用箭头函数 与 option property
api
实例property
vue内置属性以attrs,slots,$scopedSlots 等
Vue.observable( object )
watch
el
parent
extends
provide / inject
祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在其上下游关系成立的时间里始终生效。
model
虚拟dom
Vue 通过建立一个虚拟 DOM 来追踪自己要如何改变真实 DOM。
return createElement('h1', this.blogTitle)
返回不是一个实际的 DOM 元素。它更准确的名字可能是 createNodeDescription
// @returns {VNode}
createElement(
// {String | Object | Function}
// 一个 HTML 标签名、组件选项对象,或者
// resolve 了上述任何一种的一个 async 函数。必填项。
'div',
// {Object}
// 一个与模板中 attribute 对应的数据对象。可选。
{
// (详情见深入数据对象节)
},
// {String | Array}
// 子级虚拟节点 (VNodes),由 `createElement()` 构建而成,
// 也可以使用字符串来生成“文本虚拟节点”。可选。
[
'先写一些文字',
createElement('h1', '一则头条'),
createElement(MyComponent, {
props: {
someProp: 'foobar'
}
})
]
)