数据驱动:将数据和视图相关联,当数据发生变化时,能够自动更新视图。
单页利用 VS 多用利用
1) 多页利用:
长处:首屏工夫快,SEO成果好
毛病:页面切换慢
2) 单页利用:
长处:页面切换快
毛病:首屏工夫稍慢,SEO差
计算属性
依赖条件不变的前提下,第一次计算的后果会被缓存起来,前面再应用时间接应用第一次计算的后果。
计算属性当其依赖属性的值发生变化时,这个属性的值会自动更新,与之相干的DOM局部也会同步自动更新。
1)计算属性两种模式
- 函数模式
-
对象模式,具备get()和set()办法
computed: { url: { get() { return `${this.protocol}//${this.host}`; }, set(val) { const tmp = val.split('//'); if (tmp.length > 1) { this.protocol = tmp[0]; this.host = tmp[1]; } } } }
2)计算属性利用闭包传参
computed: {
url() {
return function(hash) {
return `${this.protocol}//${this.host}#${hash}`;
}
}
}
侦听器
1.函数模式
2.对象模式
watch: {
location: {
immediate: true, // 在初始化时执行
deep: true, // 递归(深度)遍历
handler(newVal, oldVal) {
this.url = `${newVal.protocol}//${newVal.host}`;
}
}
}
computed和watch的区别
计算属性和侦听器的本质都是Watcher的实例。
computed基于响应式依赖进行缓存,只在相干响应式依赖产生扭转时才会从新求值。
在依赖不变的状况下,计算属性应用缓存的上次的计算结果,而不会反复执行
watch当须要在数据变动时执行异步或开销较大的操作时,应用watch。
v-if和v-show
v-if 是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。
v-if动静增删节点(DOM),v-show管制CSS属性(display)进行切换;
v-if是惰性的,初始条件为false时不会参加模板编译;v-show会始终参加渲染;
v-if会缓存编译后果,当v-if条件为true时,vue会编译为虚构DOM并缓存,待下一次条件渲染间接应用。
v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因而,如果须要十分频繁地切换,则应用 v-show 较好;如果在运行时条件很少扭转,则应用 v-if 较好。
v-if中的key
Vue 会尽可能高效地渲染元素,通常会复用已有元素而不是从头开始渲染。
给元素增加一个具备惟一值的 key attribute,示意元素是齐全独立的,不要复用它们。
<template v-if="loginType === 'username'">
<label>Username</label>
<input placeholder="Enter your username" key="username-input">
</template>
<template v-else>
<label>Email</label>
<input placeholder="Enter your email address" key="email-input">
</template>
v-if和v-for
同一元素上,v-for优先级高于v-if,即先执行遍历,再执行判断。
若条件呈现在循环外部,可通过计算属性computed提前过滤掉那些不须要显示的项。
v-for中的key值的作用:作为同级元素的标识,进步更新效率。
key值的作用先要理解vue更新DOM的机制,即Diff算法。该算法有三个假如, 其中一个是同级元素能够通过惟一id值辨别,而key值的作用即作为这个id值,使更新(包含插入,删除,新增,替换等等)时,vue可能甄别到底是哪些虚构DOM产生了变动,而不是抽象的批量更新。
应用 of 作为分隔符,因为它更靠近 JavaScript 迭代器的语法:
<div v-for="item of items" :key="item.id"></div>
变异办法和非变异办法
变异办法:Vue将被侦听的数组的变更办法进行了改写,会变更原始数组
push()、pop()、unshift()、shift()、splice()、reverse()、sort()
非变异办法:不会变更原始数组,而总是返回一个新数组
filter()、concat() 和 slice()
当应用非变更办法时,能够用新数组替换旧数组
事件
<button @click="add(step, $event)"></button>
methods: {
add(step, event) {
this.count += step;
console.log(event); // 拜访原生事件对象
}
}
事件修饰符:
.stop // 阻止事件冒泡
.prevent // 阻止浏览器默认行为
.self // 当触发事件的对象(event.target)是本身才执行
.capture // 监听器采纳事件捕捉模型
.once // 监听器触发一次后移除
.passive // 通知浏览器该监听器是“被动的”,不会阻止浏览器默认行为,让滑动更顺畅。尤其可能晋升挪动端的性能。
数据双向绑定(v-model)
v-model指令用来在input、select、text、checkbox、radio等表单控件元素上创立双向数据绑定。
v-model是语法糖,在用户输出事件中更新数据。
v-model增加.lazy修饰符将监听change事件.native
修饰符用于在一个组件的根元素上监听原生事件 // 给组件绑定原生事件
vue-validator表单校验插件
组件化开发
指标:为了可重用性高,缩小重复性的开发,即一处开发,多处应用。
单向数据流:父组件向子组件传值
1) is属性, 在table、ul、ol、select中应用
有些 HTML 元素,诸如 <ul>、<ol>、<table> 和 <select>,对于哪些元素能够呈现在其外部是有严格限度的。而有些元素,诸如 <li>、<tr> 和 <option>,只能呈现在其它某些特定的元素外部。
<table>
<tbody>
<tr is="row"></tr> // row组件
</tbody>
</table>
2) 子组件中data必须是一个函数,且返回一个对象
data() {
return {
list: []
}
}
vue组件可能会有很多个实例,采纳函数返回一个全新data模式,使每个实例对象的数据不会受到其余实例对象数据的净化。
- 根实例对象data能够是对象也能够是函数(根实例是单例),不会产生数据净化状况。
- 组件实例对象data必须为函数,目标是为了避免多个组件实例对象之间共用一个data,产生数据净化。采纳函数的模式,initData时会将其作为工厂函数都会返回全新data对象。
3) 动静组件
在不同组件之间进行动静切换
<component :is="type"></component> // type为组件名变量
组件间通信
1) 父子组件通信:props和$emit
2) 兄弟组件通信:事件总线(EventBus)
$emit触发事件,$on监听,$off()用来删除事件监听器
非父子组件间传值( Bus | 总线 | 公布订阅模式 | 观察者模式)
// eventBus.js
Vue.prototype.eventBus = new Vue();
// 组件A
this.eventBus.$emit('send', text); // 触发
// 组件B
this.eventBus.$on('send', (text) => { // 监听
this.text = text;
});
3) Vuex:状态管理工具
集中式状态治理,对全局共享状态数据进行治理和操作。应用场景:
1)多个view依赖同一个状态(state)
2)多个view要扭转同一个状态(state)
store是管理器
state:状态
mutations:更改Vuex的store中的状态的惟一办法,同步
actions:异步
getters:相似于计算属性
组件不容许间接批改store里的state,用commit函数提交mutation办法来批改数据。
插槽
<slot>
作为咱们想要插入内容的占位符
1) slot
<div>
<h1>title</h1>
<slot></slot>
<p>说点啥</p>
</div>
2) 具名插槽
// page组件
<div class="page">
<slot name="header"></slot>
<slot>default content</slot>
<slot name="footer"></slot>
</div>
// 应用page组件
<page>
<page-header slot="header"></page-header>
<main class="page__main">page content……</main>
<page-footer slot="footer"></page-footer>
</page>
3) 作用域插槽(带数据的插槽) // 罕用于表格
有时让插槽内容可能拜访子组件中才有的数据是很有用的。当一个组件被用来渲染一个我的项目数组时,这是一个常见的状况,咱们心愿可能自定义每个我的项目的渲染形式。
<slot :list="list"></slot>
// 应用作用域插槽
<template slot-scope="scope">
<p v-for="item of scope.list" :key="item.id">{{item.name}}</p>
</template>
可参考iView组件库
自定义指令
残缺的指令:v-name:foo.a=”expression”
其中name: 指令名称,foo:参数,a:修饰符,expression:表达式。
1) 全局注册
-
回调函数模式
Vue.directive('test', function(el, bindings, vnode) { console.log(el, bindings); })
-
对象模式 — 钩子函数
Vue.directive('test', { bind(el, bindings, vnode) { // 初始化数据 console.log('bind') }, inserted(el, bindings, vnode) { // 获取父级的DOM元素 console.log('inserted') }, update(el, bindings, vnode) { // 监听绑定的组件的变动 console.log('update'); }, componentUpdated(el, bindings, vnode) { // 监听绑定的组件的变动 console.log('componentUpdated'); }, unbind(el, bindings, vnode) { // 组件销毁时触发 console.log('unbind'); }, })
2) 部分注册
directives: { test: { // 同上 } }
3) 长按指令
// 长按指令定义 Vue.directive('long-press', function(el, bindings) { let timer = null; const value = bindings.value; const duration = value.duration || 700; // 长按工夫 const fn = value.callback; // 回调函数 const _this = this; el.addEventListener('touchstart', run); el.addEventListener('touchend', stop); el.addEventListener('touchmove', stop); function run() { if (timer === null) { timer = setTimeout(() => { fn && fn.call(_this, el, value); clearTimeout(timer); }, duration); } } function stop() { // 勾销 -- 革除定时器 clearTimeout(timer); timer = null; } }) // 长按指令应用 // 用value作为对象的形式给long-press指令传参 <ul> <li v-for="item of list" :key="item.id" v-long-press="{ callback: handleLongPress, item: item, duration: 500 }" ></li> </ul> handleLongPress(el, value) { console.log(value); // do sth }
生命周期
一个事物从诞生,到倒退,继续,直至最初销毁的过程,叫它的生命周期。
实例创立 => 挂载 => 数据变动 => 更新视图 => 销毁
1) $nextTick([callback]) 设置一个回调,在下次DOM更新之后执行数据变动后,视图不会立刻更新。
2) $forceUpdate() 使实例从新渲染,更新视图。
3) 生命周期钩子:
beforeCreate:组件实例还未创立
created:实例创立实现,初始化数据($data),实现配置,罕用于异步数据获取
beforeMount:render函数渲染,未执行渲染、更新,dom未创立
mounted:挂载实现,dom已创立,可用于获取拜访数据和dom元素。利用:eg: 上报页面拜访人数
beforeUpdate时,虚构DOM曾经生成
updated:该实例更新实现 // 若心愿子组件等更新实现,在$nextTick([callback])中解决
beforeDestroy:销毁之前,可用于一些定时器或订阅的勾销
destroyed:销毁实现
渲染
1) el挂载
2) template模板
3) 自定义render
无论哪种渲染写法在执行$mount时都是将生成render函数。
Render函数调用_render => 虚构DOM调用_update => 实在DOM
模板编译的过程:
parse生成AST语法树,optimize优化,generate生成render函数code。 // AST形象语法树
parse的作用是把模板内容转化成AST语法树,不便程序剖析;
optimize的作用是优化语法树,通过标记动态节点的形式;
generate生成render函数的code,是字符串类型。
模板编译是为了生成render函数,并不会执行render函数,render生成的是虚构DOM。
DOM更新:
渲染watcher的回调函数updateComponent实现了DOM更新
_render生成虚构DOM
_update通过比拟虚构DOM更新实在DOM
数据变动时才可能触发渲染watcher执行更新
Vue-Router
- 扭转URL不向服务器发送申请
- 前端监听URL变动
- 解析URL并执行相应操作
三种模式:hash、history、abstract
abstract:实用于所有JavaScript环境,例如服务器端应用Node.js。若没有浏览器API,路由器将主动被强制进入此模式。
1) hash
window.location.assign()
window.location.replace()
window.onhashchange // hashchange事件
2) history
window.history.pushState()
window.history.replaceState()
window.onpopstate // pushState和replaceState不会触发popstate事件
history.pushState/history.replaceState不会触发popstate事件,只扭转url和历史记录
history模式采纳的是实在路由,须要服务器重定向到主页,否则刷新浏览器还是会向服务器发送申请,报404
3) hash与history的区别
hash:丑(#),hash会占用锚点性能,兼容性较好
history:路由与后端无异,IE10及以上,须要后端反对
4)Router API
$router办法:
$router.push()
$router.replace()
$router.go()
$router.forward()
$router.back()
router.push/router.replace在不同模式下调用的底层API不同,一个扭转实在url门路,一个只扭转hash。
$route参数:
$route.params -- 动静路由匹配的参数
$route.query -- 查问参数
$route.hash -- url的hash值
$route.fullPath -- url全门路
$route.meta -- 与页面关联的元信息
5)parse location
parseLocation(location) {
const { path, query, hash } = location;
return `${path}?${parseQuery(query)}#${hash}`;
}
parseQuery(query) {
return Object.keys(query).map(key => {
return `${key}=${query[key]}`;
}).join('&');
}
发表回复