Vue模板
- 语法名称:插值表达式
- 表现形式:{{content}}
- content 可以是:变量、函数、函数调用、算术运算
在Vue模板中,自动绑定了上下文对象到当前组件实例对象中,即模板可访问实例中定义的数据,访问data可以省略this
模板与渲染
template
- 每一个独立的足见模板有且仅有一个顶层父级元素
let app = new Vue({
# 错误,顶级只能有一个div:
template:`<div>hello world</div><div>hello world</div>`
# 正确:
template:`<div>hello world</div>`
data:{
name:'xiaoming'
}
})
app.$mount('#app')
-
vue-cli中一般用不到---> 如果值以 # 开始,则它将被用作选择符,并使用匹配元素的 innerHTML 作为模板。常用的技巧是用
<script type="x-template">
包含模板。
el
- 与$mount的作用一致
-
支持的数据类型:
string | Element
-
限制:只在用
new
创建实例时生效。 -
特性:
-
提供一个在页面上已存在的 DOM 元素作为 Vue 实例的挂载目标。可以是 CSS 选择器,也可以是一个 HTMLElement 实例。
-
在实例挂载之后,元素可以用
vm.$el
访问。 -
如果在实例化时存在这个选项,实例将立即进入编译过程,否则,需要显式调用
vm.$mount()
手动开启编译。
-
render
-
类型:
(createElement: () => VNode) => VNode
-
详细:
字符串模板的代替方案,允许你发挥 JavaScript 最大的编程能力。该渲染函数接收一个
createElement
方法作为第一个参数用来创建VNode
。如果组件是一个函数组件,渲染函数还会接收一个额外的
context
参数,为没有实例的函数组件提供上下文信息。
-
-
html -->vNode(Vue的VDOM)
通过原生的纯对象来间接描述一个真实 DOM 对象,只提供必要的信息,同时在需要更新的时候,会加入一些算法(diff)来比较上一次的 VDOM 与 更新后的 VDOM 之间的差异,找出确实需要更新点,最后 再统一反馈 去更新对应的真实 DOM。 -
对比之下, 直接操作原生 DOM 对象有以下缺点:
- 结构信息复杂
- 操作耗时
- 消耗资源
除此之外还有其它缺点,而且原生DOM中许多的属性,在开发时也并不需要关心。因此,最好不频繁的直接操作原生DOM。
-
-
render函数执行流程
html --> AST(将HTML抽象成语法树对象) --> 生成渲染函数 --> vNode --> DOM
createElement函数的官网解析
let app = new Vue({
el: '#app'
render(h) {
return createElement(h) {
'div',
{
attrs: {
id: 'app'
}
},
[
createElement('h1', 'hello world')
]
};
}
})
编译顺序
- 自动调用
$mount
。 - 指定
el
同时又没有指定template
,则把el
的outerHTML
作为template
。
源码节选:
HTML-->AST-->render
// template-->el
var template = options.template
if (template) {
if (typeof template === 'string') {
}
else if (template.nodeType) {
template = template.innerHTML
}
else {
warn()
}
return this
}
else if (el) {
template = getOuterHTML(el);
}
if (template) {
if (config.performance && mark) {
mark('compile')
}
}
// AST的具体实现。把template的html字符串,解析成对应的render函数
// ref 是编译后返回的对象,就含有render函数
var ref = compileToFunctions(template, {
...
}, this)
var render = ref.render;
// 赋值给options.render,等待调用
options.render = render;
render的调用
if (vm.$options.el) {
vm.$mount(vm.$options.el)
}
Vue.prototype.$mount = function (
el,
hydrating
) {
el = el && isBrowser ? query(el) : undefined;
return mountComponent(this , el, hydrating)
};
Vue.prototype.$mountComponent = function (
vm,
el,
hydrating
) {
// 挂载el属性到对象
vm.$el = el;
if (!vm.$options.render) {
vm.$options.render = createEmptyVNode;
}
callHook(vm, 'beforeMount')
var updateComponent;
if (config.performance && mark {
}
else {
// 执行_render函数,得到vnode对象
// 执行_update函数更新真实dom
vm._update(vm._render(), hydrating)
}
)
}
模板编译顺序
在源码中,Vue增加了对初始化是否成功的判断,如果非new Vue()创建的对象,Vue会报warn。
// 初始化:
Vue()
// Vue内部的this指向window或者undefined(严格模式下)
初始化工作是通过_init(options)函数完成的。
模板 与 Vue响应式
-
响应式
- (数据发生变化,Vue会拦截并处理),是Vue有别于普通模板引擎的重要特性。
-
Vue2
中响应式数据实现是建立在Object.defineProperty
方法上,由于该方法的本身的一些特性,在拦截数据处理上会有一些问题需要注意:- 对象新增属性无法拦截。
- 数组变动无法拦截。
- vue拦截了数组方法:push、pop、shift、unshift、splice、sort、reverse。为方法扩展了
ob.dep.notify();
,即数据变化时发出通知,跟新数据。 - 利用索引直接设置一个数组项时。
- 解决:Vue.set()、(new Vue()).$set(),两者等价。
- vue拦截了数组方法:push、pop、shift、unshift、splice、sort、reverse。为方法扩展了
- 修改数组的长度时。
- 解决:方法变异(Vue 内部已处理)。
-
Vue3
中使用proxy替换了Object.defineProperty
。
-
- (数据发生变化,Vue会拦截并处理),是Vue有别于普通模板引擎的重要特性。
-
当数据发生改变,Vue会自动跟新有关视图(只更新有变化的位置,最小化的更新视图,减少真实dom操作),让开发人员专注于数据和业务的处理。
-
Vue的响应式和性能优化,主要表现在以下特性:
- vdom(不直接操作真实DOM)
- diff算法(计算得出最小化真实DOM的操作)
当然,vdom改变并不一定会引起真实DOM操作。在适当情况下,新vdom会经过diff算法,与旧vodm比较,得到最小变动后,才会进行一次真实DOM操作。
小结:
- 编译顺序:template -> el -> render
- 直接编写render函数生成dom,最高效
网友评论