技术栈知识点巩固——Vue
# Vue 知识点总结
# Vue 多模块管理路由(Router)、请求(Axios)
# Vue 组件开发打包、Vue 项目打包、js库组件库打包使用
Vue 概念
Vue 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。
Vue核心
Vue.js 的核心是一个允许采用简洁的模板语法来声明式地将数据渲染进 DOM 的系统。
MVVM 模型
Mode-View-ViewModel Model 代表数据模型、View 代表UI 组件、ViewModel 是View 和Model 层的桥梁,数据会绑定到viewModel 层并自动将数据渲染到页面中,视图变化的时候会通知viewModel 层更新数据
Vue 生命周期
Vue 实例有?个完整的?命周期,也就是从开始创建、初始化数据、编译模版、挂载Dom -> 渲染、更新 -> 渲染、卸载 等?系列过程,称这是Vue 的?命周期。
beforeCreate(创建前)
created(创建后)
- 组件实例已经完全创建,属性也绑定,真实
dom 还没有生成,$el 还不可用
beforeMount(挂载前)
- 在挂载开始之前被调用:相关的
render 函数被首次调用
mounted(挂载后)
- 在
el 被新创建的vm.$el 替换,并挂载到实例上去之后调用该钩子
beforeUpdate(更新前)
updated(更新后)
activated(激活前)
deactivated(激活后)
beforeDestory(销毁前
destoryed(销毁后)
Vue 父向子传值
Vue 子向父传值
父子组件生命周期顺序
- 执行顺序:父组件先创建,然后子组件创建;子组件先挂载,然后父组件挂载,即
父beforeCreate-> 父create -> 子beforeCreate-> 子created -> 子mounted -> 父mounted
非父子组件通信
import Vue from 'vue'
export default new Vue()
import eventBus from './EventBus'
methods: {
pushMsg(){
eventBus.$emit('pushMsg',this.childNum++)
}
}
mounted() {
eventBus.$on('pushMsg', (children1Msg) => {
this.msg = children1Msg
})
}
Vue.$nextTick()
- 参数:
{Function}[callback] - 用法:将回调延迟到下次
DOM 更新循环之后执行。在修改数据之后立即使用它,然后等待 DOM 更新。它跟全局方法Vue.nextTick 一样,不同的是回调的 this 自动绑定到调用它的实例上。
示例
<!DOCTYPE html>
<html>
<body>
<head>
<script src="./js/vue.js"></script>
</head>
<div id="app">
<p id="msg">{{message}}</p>
<button @click="change">更新</button>
</div>
</body>
</html>
<script>
var app = new Vue({
el: '#app',
data: {
message: 'Hello Vue!'
},
methods: {
change() {
this.message = 'This is new message!';
console.log('not nextTick():', document.getElementById("msg").innerHTML);
}
}
})
</script>
-
console.log 预期应该输出not nextTick():This is new message! 但是输出的是 not nextTick(): Hello Vue! -
Vue 在更新DOM 的时候,是异步执行的。只要监听到数据变化,vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。如果同一个watcher 被多次触发,只会被推入到队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和 DOM 操作是非常重要的。然后,在下一个的事件循环tick 中,Vue 刷新队列并执行实际 (已去重的) 工作。
使用
change(){
this.message = 'This is new message!';
this.$nextTick(()=>{
console.log('use nextTick():',document.getElementById("msg").innerHTML);
})
console.log('not nextTick():',document.getElementById("msg").innerHTML);
}
- 使用
this.$nextTick() 之所以能获取到更新后的值,并不是改变了vue 的渲染流程,而是改变了获取最新值的时间,并不是立即获取,而是等vue 渲染完后再获取,即异步获取
Computed
- 支持缓存,只有依赖的数据发生变化,才会重新计算
- 不支持异步,当
Computed 中有异步操作时,无法监听数据的变化 Computed 的值默认会走缓存- 如果一个属性是由其他属性计算而来的,这个属性依赖其他的属性,一般会使用
computed - 如果
computed 属性的属性值是函数,那么默认使用get 方法,函数的返回值就是属性的属性值;在computed 中,属性有一个get 方法和一个set 方法,当数据发生变化时,会调用set 方法。
Watch
- 不支持缓存,数据变化时,触发相应的操作
- 支持异步监听
- 监听的函数接收两个参数,第一个时参数的新值,第二个是变化之前的值
- 监听数据必须是
data 中声明的或者父组件传递过来的props 中的数据,当发生变化时,会触发其他操作,函数有两个的参数:
immediate :组件加载立即触发回调函数deep :深度监听,发现数据内部的变化,在复杂数据类型中使用,例如数组中的对象发生变化。需要注意的是,deep 无法监听到数组和对象内部的变化。
watch: {
value () {
}
}
watch: {
obj: {
handler () {
},
deep: true,
immediate: true
}
}
常见的事件修饰符
.stop :等同于 JavaScript 中的 event.stopPropagation() ,防止事件冒泡;.prevent :等同于 JavaScript 中的 event.preventDefault() ,防止执行预设的行为(如果事件可取消,则取消该事件,而不停止事件的进一步传播);.capture :与事件冒泡的方向相反,事件捕获由外到内;.self :只会触发自己范围内的事件,不包含子元素;.once :只会触发一次。
v-show、v-if
-
v-show 隐藏则是为该元素添加css--display:none ,dom 元素依旧还在。v-if 显示隐藏是将dom 元素整个添加或删除 -
v-if 切换有一个局部编译/卸载的过程,切换过程中合适地销毁和重建内部的事件监听和子组件;v-show 只是简单的基于css 切换 -
v-if 是真正的条件渲染,它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。只有渲染条件为假时,并不做操作,直到为真才渲染 -
v-show 由false 变为true 的时候不会触发组件的生命周期 -
v-if 由false 变为true 的时候,触发组件的beforeCreate 、create 、beforeMount 、mounted 钩子,由true 变为false 的时候触发组件的beforeDestory 、destoryed 方法 -
v-if 有更高的切换消耗;v-show 有更高的初始渲染消耗;
Vue 动态组件
Vue.js 提供了一个特殊的元素 <component> 用来动态地挂载不同的组件 使用 is 属性来选择要挂载的组件。
<template>
<div>
<h1>vue 动态组件使用</h1>
<button @click="changeView('A')">切换到组件A</button>
<button @click="changeView('B')">切换到组件B</button>
<button @click="changeView('C')">切换到组件C</button>
<component :is="currentView"></component>
</div>
</template>
<script>
export default {
components: {
componentA: {
template: '<div>组件A</div>'
},
componentB: {
template: '<div>组件B</div>'
},
componentC: {
template: '<div>组件C</div>'
}
},
data () {
return {
currentView: 'componentA'
}
},
methods: {
changeView (val) {
this.currentView = 'component' + val
}
}
}
</script>
keep-alive
keep-alive 可以实现组件缓存,当组件切换时不会对当前组件进行卸载。- 常用的两个属性
include/exclude ,允许组件有条件的进行缓存。 - 两个生命周期
activated/deactivated ,用来得知当前组件是否处于活跃状态。 keep-alive 的中还运用了LRU(Least Recently Used) 算法。
不需要响应式的数据应该怎么处理
data () {
this.list1 = { xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx }
return {}
}
data () {
return {
list1: Object.freeze({xxxxxxxxxxxxxxxxxxxxxxxx}),
}
}
Vue中双向数据绑定实现
vue 双向数据绑定是通过 数据劫持 结合 发布订阅模式的方式来实现的, 也就是说数据和视图同步,数据发生变化,视图跟着变化,视图变化,数据也随之发生改变;核心:关于VUE 双向数据绑定,其核心是 Object.defineProperty() 方法。- 使用
Object.defineProperty() 来定义属性的set函数,属性被赋值的时候,修改Input 的value 值以及span 中的innerHTML ;然后监听input 的keyup 事件,修改对象的属性值,即可实现这样的一个简单的数据双向绑定。
<!DOCTYPE html>
<html>
<head>
<script src="./js/vue.js"></script>
</head>
<body>
<h2>Vue 双向绑定</h2>
<input type="text" id="textInput" />
输入:<span id="textSpan"></span>
</body>
<script>
let obj = {};
let textInput = document.getElementById("textInput");
let textSpan = document.getElementById("textSpan");
Object.defineProperty(obj, "foo", {
set: function (newValue) {
textInput.value = newValue;
textSpan.innerHTML = newValue;
},
});
textInput.addEventListener("keyup", function (e) {
obj.foo = e.target.value;
});
</script>
</html>
Vue 优化
编码阶段
SEO优化
打包优化
- 压缩代码
Tree Shaking /Scope Hoisting - 使用
cdn 加载第三方模块 - 多线程打包
happypack splitChunks 抽离公共文件sourceMap 优化
v-model
v-model 用于表单数据的双向绑定,是一个语法糖v-bind 绑定一个value 属性v-on 指令给当前元素绑定input 事件
<input v-model="sth" />
<input v-bind:value="sth" v-on:input="sth = $event.target.value" />
vue组件中data是一个函数
- 组件中的
data 是一个函数数据以函数返回值的形式定义,这样每次复用组件的时候,都会返回一份新的data ,每个组件都会有自己私有的数据空间,他们只负责各自维护的数据,不会造成混乱。
vue-router 钩子
全局路由钩子
router.beforeResolve : 在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,该钩子函数就被调用router.afterEach : 路由改变后的钩子
beforeEach(to,from,next)
afterEach(to,from,next)
单个路由钩子
cont router = new Router({
routes: [
{
path: '/file',
component: File,
beforeEnter: (to, from ,next) => {
}
}
]
});
组件内路由钩子
beforeRouteEnter 不能获取组件实例 this ,因为当守卫执行前,组件实例被没有被创建出来,剩下两个钩子则可以正常获取组件实例 this
methods: {},
beforeRouteLeave (to, from, next) {}
beforeRouteEnter 在进入当前组件对应的路由前调用beforeRouteUpdate 在当前路由改变,但是该组件被复用时调用beforeRouteLeave 在离开当前组件对应的路由前调用
单向数据流和双向数据流
-
单向数据流是指数据只能从父级向子级传递数据,子级不能改变父级向子级传递的数据。 -
双向数据流是指数据从父级向子级传递数据,子级可以通过一些手段改变父级向子级传递的数据。 -
比如用v-model 、.sync 来实现双向数据流。
vue-loader
- 基于
webpack 的一个的loader ,解析和转换 .vue 文件,提取出其中的逻辑代码 script 、样式代码 style 、以及 HTML 模版 template ,再分别把它们交给对应的 Loader 去处理,核心的作用,就是提取,划重点。
Vue属性改变页面不刷新
-
Vue 不允许在已经创建的实例上动态添加新的响应式属性若想实现数据与视图同步更新,可采取下面三种解决方案: -
Vue.set() -
Object.assign() -
$forcecUpdated() -
如果为对象添加少量的新属性,可以直接采用Vue.set() -
如果需要为新对象添加大量的新属性,则通过Object.assign() 创建新对象 -
如果你实在不知道怎么操作时,可采取$forceUpdate() 进行强制刷新 (不建议)
Vue mixin 使用
局部混入
export const mixins = {
data () {
return {
msg: 'vue mixin 测试'
}
},
computed () {},
created () {
console.log('mixin 中 created 生命周期函数')
},
mounted () {
console.log('mixin 中 mounted生命周期函数')
},
methods: {
clickMe () {
console.log('mixin 中 点击事件')
}
}
}
<template>
<div>
<el-button @click="clickMe">点击按钮</el-button>
</div>
</template>
<script>
import { mixins } from './index.js'
export default {
name: 'mixin-vueone',
mixins: [mixins],
components: {},
data () {
return {
}
},
created () {
console.log('组件调用 mixin 数据', this.msg)
},
mounted () {
console.log('我是组件的 mounted 生命周期函数')
},
methods: {
}
}
</script>
全局混入
import { mixins } from "./mixin/index";
Vue.mixin(mixins);
- 谨慎使用全局混入,因为它会影响每个单独创建的
Vue 实例 (包括第三方组件)。大多数情况下,只应当应用于自定义选项,就像上面示例一样。推荐将其作为插件发布,以避免重复应用混入。
优点
- 提高代码复用性
- 无需传递状态
- 维护方便,只需要修改一个地方即可
缺点
- 命名冲突
- 滥用的话后期很难维护
- 不好追溯源,排查问题稍显麻烦
- 不能轻易的重复代码
插槽
- 在一个组件标签中,加入一些内容,那就必须要在组件内声明
slot 元素,否则不会被渲染出来。
默认插槽
<template>
<div>
<h3>{{ title }}</h3>
<slot name="center">center 部分</slot>
<slot name="fotter">fotter 部分</slot>
</div>
</template>
<script>
export default {
name: 'childvueone',
data () {
return {
}
},
props: ['title', 'listData']
}
</script>
<template>
<div>
<childvueone title="测试1">
<span slot="center">center 的内容1</span>
<a slot="fotter">更多</a>
</childvueone>
<childvueone title="测试2">
<span slot="center">center 的内容</span>
<div slot="fotter">
<span>插槽内容2</span>
</div>
</childvueone>
</div>
</template>
<script>
import childvueone from './childvue-one.vue'
export default {
components: {
childvueone
},
data () {
return {
}
}
}
</script>
具名插槽
- 没有使用
name 属性的slot 插槽就叫做匿名插槽,也被称为默认插槽
作用域插槽
- 作用域插槽是一种特殊类型的插槽,用作一个(能被传递数据的)可重用模板,来代替已经渲染好的元素。利用
slot 标签将子组件的数据传递到分发的内容上,类似于父子组件传值的prop 传递数据。
Mustache表达式使用
-
双花括号{{}} 就是 mustache 语法,用于展示data 中的内容,mustache 中可以出现任意的 JS 表达式; -
表达式{{}} 只能从数据对象data 中获取数据; -
mustache 中不能出现语句,比如:if () {} / for(var i =0 ...) {} / var num = 1; -
Mustache 语法不能作用在 HTML 元素的属性上;
ref属性
- 获取
dom 元素/组件:标签上添加ref 属性,this.$refs.ref 属性值获取该dom元素/组件 。 this.$refs.ref 属性值.变量名 获取组件中的数据this.$refs.ref 属性值.方法名() 获取组件中的方法
$parent 和 $children
this.$parent 获取父组件$children 由于子组件的个数不确定 返回的是一个数组 ,不是对象this.$children[0] 获取第一个子组件
路由模式
hash 模式:通过#号 后面的内容的更改,触发hashchange 事件,实现路由切换history 模主要是通过history Api 的pushState() 和replaceState() 两个方法来实现的.pushState() 可以改变url 地址且不会发送请求,replaceState() 可以读取历史记录栈,还可以对浏览器记录进行修改。
history.go(-2);
history.go(2);
history.back();
hsitory.forward();
Vue template 编译过程
- 解析
template 生成ast (抽象语法树) optimize ,对AST 进行优化,标注静态节点generate ,根据AST 生成render 函数
|