vue总结

84次阅读

共计 5154 个字符,预计需要花费 13 分钟才能阅读完成。

原文地址

vue(前端框架)解决了什么问题?

现在的前端页面元素越来越多,结构也变得越来越复杂, 当数据和视图混合在一起的时候对它们的处理会十分复杂,同时也很容易出现错误,而现代框架 使用声明式语法,描述组件对象的嵌套关系,并自动生成与 dom 对象的对应关系
参考 1

vue 生命周期

vue 生命周期 描述
beforeCreate 组件实力被创建,el 和数据对象都为 undefined,还未初始化
create 数据已经被初始化,并且初始化了 Vue 内部事件, 但是 DOM 还未生成
befroeMount 完成了模板的编译。把 data 对象里面的数据和 vue 的语法写的模板编译成了虚拟 DOM
mouted 执行了 render 函数,将渲染出来的内容挂载到了 DOM 节点上
beforeUpdate 组件更新之前: 数据发生变化时,会调用 beforeUpdate,然后经历 DOM diff
updated 组件更新后
actived keep-alive组件被激活
deactivated keep-alive移除
beforeDestroy 组件销毁前
destroyed 组件销毁后

简述 Vue 的响应式原理

可以问数据变动如何和视图联系在一起?
Vue 是采用数据劫持结合发布者 - 订阅者模式的方式, Vue 相应系统有三大核心:observe,dep,watcher; 精简版 Vue 代码参考

  • Observe:当一个 Vue 实例创建时,initData 阶段,vue 会遍历 data 选项的属性(observe),用 Object.defineProperty 将它们转为 getter/setter 并且在内部追踪相关依赖(dep),在属性被访问和修改时通知变化。
  • Compite:调用 compile 方法解析模版, 当视图中有用到 vue.data 中数据的时候,会调用实例化 watcher 方法进行依赖收集
  • Watcher:是 ObserverCompile之间通信的桥梁,当视图中遇到绑定的数据时, 在 watcher 方法中会获取这个数据,此时会触发 observe 中的 getter 方法,
  • Dep:发布订阅模式,observe中数据的 getter 被触发时会收集依赖的watcher(dep.depend 方法)
  • 当有数据被改动时会触发 observe 中数据的 setter,此时会调用dep.notify 方法给所有订阅的 watcher 发通知(通过回掉方式)进行视图更新,此时会进行 diff 流程:

vue 中 data 为什么必须要是一个函数

vue 中的 data 为对象,是引用类型,当重用组件时,一个组件对 data 做了更改,那么另一个组件也会跟着改,而使用返回一个函数返回数据,则每次返回都是一个新对象,引用地址不用,所以就不会出现问题

Virtual DOM 是什么

虚拟 DOM 是一个 JavaScript 对象,包含了当前 DOM 的基本结构和信息,它的存在是为了减少对操作无用 DOM 所带来的性能消耗,在大量的、频繁的数据更新下能够对视图进行合理的高效的更新(细粒度的精准修改),同时也抽象了原来的渲染过程,实现了跨平台的能力

简述 vue 中的 DOM DIFF 算法

精简源码;当数据发生改变时,set方法会让调用 Dep.notify 通知所有订阅者 Watcher,订阅者就会调用patch 给真实的 DOM 打补丁 (两个重要函数patchVnodeupdateChildren):

  • 先判断根结点及变化后的节点是否是sameVnode,如果不是的化,就会创建新的根结点并进行替换
  • 如果是 sameVnode,则进入patchVnode 函数,其基本判断

    1. 如果两个节点是相等 oldVnode === vnode 则直接return
    2. 如果 新节点是文本节点 ,则判断新旧文本节点是否一致,不一致(oldVnode.text !== vnode.text) 则替换
    3. 如果 新节点不是文本节点 ,则开始比较新旧节点的子节点oldChch
    4. 如果 子节点都存在 ,则进行updateChildren 计算(稍后讲)
    5. 如果 只有新子节点存在, 则如果旧节点有文本节点,则移除文本节点,然后将新子节点拆入
    6. 如果 只有旧子节点存在,则移除所有子节点
    7. 如果 均无子节点且旧节点是文本节点,则移除文本节点(此时新节点一定不是文本节点)
  • updateChildren函数做细致对比

    1. start && oldStart 对比
    2. end && oldEnd 对比
    3. start && oldEnd 对比
    4. end && oldStart 对比
    5. 生成 map 映射,(key: 旧子节点上的 key,value: 旧子节点在自己点中的位置), 根据 key 记录下老节点在新节点的位置(idxInOld
      1) 如果 找到了 idxInOld, 如果是 相同节点 则移动旧节点到新的对应的地方,否则虽然 key 相同但元素不同,当作新元素节点去创建
      2) 如果 没有找到 idxInOld,则创建节点
    6. 如果 老节点先遍历完,则新节点比老节点多, 将新节点多余的插入进去
    7. 如果 新节点先遍历完,则就节点比新节点多, 将旧节点多余的删除

vue 中 key 的作用

主要是为了复用节点,高效的更新虚拟 DOM,另外,在使用标签元素过渡效果时也会用到 key

computed 的原理

  • vue 对象初始化的同时对计算属性进行初始化initComputed,
  • computed 会对初始化的 Watcher 传入 lazy: true 就会触发 Watcher 中的watcher.dirty=true(dirty 决定了当前属性是否更新),
  • 当视图中有对 computed 引用的时候会第一次执行计算属性,并将 dirty 设置为 false, 并将结果保存在this.value 中进行缓存,
  • 如果依赖没有更改,则下次获取 computed 会这直接返回 this.value, 只有当computed 所依赖的属性发生变化时会将 dirty 设置为true,并重新计算
class Watcher{
  ……
  evaluate () {this.value = this.get()
    this.dirty = false
  }
  ……
}

class initComputed{
  …… 
  // 计算属性的 getter 获取计算属性的值时会调用
    createComputedGetter (key) {return function computedGetter () {
          // 获取到相应的 watcher
        const watcher = this._computedWatchers && this._computedWatchers[key]
        if (watcher) {
             //watcher.dirty 参数决定了计算属性值是否需要重新计算,默认值为 true,即第一次时会调用一次
              if (watcher.dirty) {
                  /* 每次执行之后 watcher.dirty 会设置为 false,只要依赖的 data 值改变时才会触发
                  watcher.dirty 为 true, 从而获取值时从新计算 */
                watcher.evaluate()}
              // 获取依赖
              if (Dep.target) {watcher.depend()
              }
              // 返回计算属性的值
              return watcher.value
        }
      }
    }
  ……
}

计算属性 computed 和 watch 的区别

计算属性 顾名思义就是通过其他变量计算得来的,它的值是基于其所依赖的属性来进行 缓存 的, 只有在其所依赖的属性发生变化时才会从新求值
watch是监听一个变量,当变量发生变化时,会调用对应的方法

对 $nextTick 的理解

vue 实现响应式并不是数据一更新就立刻触发 dom 变化,而是按照一定的策略对 dom 进行更新,源码位置,原理:

  • 首先会将所有的 nextTick 放到一个函数中,然后放在 callbacks 数组中,$nextTick没有传 cb 回掉,则返回一个promise
  • 接下来就是 callbacks 的执行时机

    • 首先如果浏览器是否兼容 promise,则用promise.resolve().then 来执行callbacks
    • 如果浏览器兼容 MutationObserver, 则用实例化的MutationObserver 监听文本变化来执行回掉,
    • 如果兼容 setImmediate, 则用setImmediate(cb) 来执行回掉
    • 最后降级为用 setTimeout(fn,0) 来执行
  • vue2.5.X 版本中对于像 v-on 这样的 DOM 交互事件,默认走 macroTimerFunc,也就是,跳过第一步promise 的判断,

子组件为何不可以修改父组件传递的 Prop, 是如何监控并给出错误提示的

  • 单向数据流,易于监测数据的流动,出现了错误可以更加迅速的定位到错误发生的位置
  • initProps 时,会对 props 进行 defineReactive 操作,传入的第四个参数是自定义的 set 报错判断函数,该函数会在触发 props 的 set 方法时执行
// src/core/instance/state.js 源码路径
function initProps (vm: Component, propsOptions: Object) {
  ...
  for (const key in propsOptions) {if (process.env.NODE_ENV !== 'production') {
      ...
      defineReactive(props, key, value, () => {
        // 如果不是跟元素并且不是更新子元素
        if (!isRoot && !isUpdatingChildComponent) {
          warn(
            `Avoid mutating a prop directly since the value will be ` +
            `overwritten whenever the parent component re-renders. ` +
            `Instead, use a data or computed property based on the prop's ` +
            `value. Prop being mutated: "${key}"`,
            vm
          )
        }
      })}
    ...
  }
}
// src/core/observer/index.js
export function defineReactive (obj,key,val,customSetter,shallow) {const property = Object.getOwnPropertyDescriptor(obj, key)
  
  const getter = property && property.get
  const setter = property && property.set
  
  Object.defineProperty(obj, key, {
    ...
    set: function reactiveSetter (newVal) {const value = getter ? getter.call(obj) : val
      if (newVal === value || (newVal !== newVal && value !== value)) {return}
      if (process.env.NODE_ENV !== 'production' && customSetter) {customSetter()
      }
      if (getter && !setter) return
      if (setter) {setter.call(obj, newVal)
      } else {val = newVal}
      childOb = !shallow && observe(newVal)
      dep.notify()}
  })
}

父子组件的生命周期执行顺序

加载过程:父组件 beforeCreate => 父组件 created => 父组件 beforeMount => 子组件 beforeCreate => 子组件 created => 子组件 beforeMount => 子组件 mounted => 父组件 mounted
更新过程:父组件 beforeUpdate => 子组件 beforeUpdate => 子组件 updated => 父组件 updated
销毁过程:父组件 beforeDestroy => 子组件 beforeDestroy => 子组件 destoryed => 父组件 destoryed

vue-router 的导航解析流程

官网

  1. 导航被触发。
  2. 在失活的组件里调用离开守卫。
  3. 调用全局的 beforeEach 守卫。
  4. 在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。
  5. 在路由配置里调用 beforeEnter
  6. 解析异步路由组件。
  7. 在被激活的组件里调用 beforeRouteEnter
  8. 调用全局的 beforeResolve 守卫 (2.5+)。
  9. 导航被确认。
  10. 调用全局的 afterEach 钩子。
  11. 触发 DOM 更新。
  12. 用创建好的实例调用 beforeRouteEnter 守卫中传给 next 的回调函数。

router.beforeResolve 注册一个全局守卫。这和 router.beforeEach 类似,区别是在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用

仅代表个人见解,能力有限,如有错误会误人子弟的地方欢迎留言指出;谢谢

正文完
 0

vue-总结

83次阅读

共计 82 个字符,预计需要花费 1 分钟才能阅读完成。

生命周期钩子 (不常用):activated deactivated errorCaptured(2.5+)
errorCaptured 当捕获一个来自子孙组件的错误时被调用

正文完
 0

Vue总结

83次阅读

共计 5590 个字符,预计需要花费 14 分钟才能阅读完成。

Vue 不支持 IE8 及以下版本 1、指令:

v-bind,v-if=”seen”,v-for=”todo in todos”,v-on 指令添加一个事件监听器,<button v-on:click=”reverseMessage”> 逆转消息 </button>
2、常用指令有哪些?

v-textv-htmlv-showv-ifv-elsev-else-ifv-forv-on v-on:click=”doThis(item)”v-bind 动态地绑定一个或多个特性,或一个组件 prop 到表达式 v -bind:href=”myurl”
v-bind:id=”myid”
v-bind 可以缩写为 : 属性 , 例如 :href=”myurl”
示例 :id :class :href :title :src :style :key :value
v-bind:class=”{active: isActive}”:当 isActive 变量为 true 时,动态添加 active 类 classv-modelv-for 的使用 i 是索引 <ul>
<li v-for=”(item, i) in msg”>
<a href=”#javascript:”>{{i}} {{item.hotWord}}</a>
</li>
</ul>
msg:[
{hotWord: “zz1”},
{hotWord: “zz2”},
{hotWord: “zz3”},
{hotWord: “zz4″}
],
3、表单

<select v-model=”selected”> <option v-for=”option in options” v-bind:value=”option.value”>
{{option.text}}
</option></select><span>Selected: {{selected}}</span>new Vue({el: ‘…’, data: {
selected: ‘A’,
options: [
{text: ‘One’, value: ‘A’},
{text: ‘Two’, value: ‘B’},
{text: ‘Three’, value: ‘C’}
]
}}) 默认选中 A 的下拉框。
4、组件基础:组件是可复用的 vue 实例,5、条件渲染:

<h1 v-if=”ok”>Yes</h1><h1 v-else>No</h1>6、事件处理:

在表单 <input> <textarea> 及 <select> 元素上创建双向数据绑定它会根据控件类型自动选取正确的方法来更新元素。7、组件基础:1。父组件像子组件传值:通过 props 数组父组件:<template> <child :name=”name”></child></template><script>import child from “./child.vue” export default {
components: {child},
data(){
return {name:” 二哈 ”}
}
}</script> 子组件:<template> <div>{{name}}</div></template><script> export default {
props:[“name”]
}</script>2:子组件给父组件:父组件:<template> <child @childToParent=”reviceSondata”></child></template><script>import child from “./child.vue” export default {
components: {child},
methods:{
reviceSondata(data){
console.log(data);
}
}
}</script> 子组件:<template> <button @click=”dataTofather”> 点击 </button></template><script> export default {
data () {
return {
message: ‘ 啦啦啦啦 ’
}
},
methods:{
dataTofather(){
this.$emit(“childToParent”,this.message,true);
}
}
}</script>
8、axios 简介

axios 是一个基于 Promise 用于浏览器和 nodejs 的 HTTP 客户端,它本身具有以下特征:
从浏览器中创建 XMLHttpRequest
从 node.js 发出 http 请求
支持 Promise API
拦截请求和响应
转换请求和响应数据
取消请求
自动转换 JSON 数据
客户端支持防止 CSRF/XSRF
引入方式:
1、安装:npm install axios
2、在 main.js 文件中 引入
import axios from ‘axios’
Vue.prototype.$http = axios
在 main.js 中添加了这两行代码之后,就能直接在组件的 methods 中使用 $http 命令
methods: {
postData () {
this.$http({
method: ‘post’,
url: ‘/user’,
data: {
name: ‘xiaoming’,
info: ’12’
}
})
}
执行 GET 请求:
// 也可以通过 params 对象传递参数
$http.get(‘/user’, {
params: {
ID: 12345
}
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
执行 POST 请求
$http.post(‘/user’, {
firstName: ‘Fred’,
lastName: ‘Flintstone’
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
})

9、学习使用 Promise

https://blog.csdn.net/heshuai… 有没有一种方法可以让写法像同步,而本质是异步化呢?Promise 就出来了。一:优点和缺点可以将异步操作以同步操作流程的方式表达出来,避免了层层嵌套的回调函数。此外,Promise 对象提供统一的接口,使得控制异步操作更加容易。
Promise 也有一些缺点。首先,无法取消 Promise,一旦新建它就会立即执行,无法中途取消。其次,如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部。当处于 Pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。如何使用?:function promiseAjax(url){
let p = new Promise((resolve,reject)=>{
// 第一个参数成功的回调函数
// 第二个参数失败的回调函数

let xhr = new XMLHttpRequest();
xhr.open(‘get’,url,true);
xhr.send();
xhr.onreadystatechange=function(){
if(this.readyState===4){
if(this.status >= 200 && this.status <300 || this.status === 304){
let res = JSON.parse(this.responseText)
resolve(res);// 成功回调函数的传参;
}else{
reject(this.status)// 失败回调函数的传参;
}
}
}
})
return p
}
// 然后调用
let oData = promiseAjax(‘http://localhost:8080/yxw/MenuController_GeneralMenuList.do’);
let oData2 = promiseAjax(‘http://localhost:8080/yxw/DomesticProfitController_getOperatIdList.do’);

Promise.all([oData,oData2]).then(function(res){
console.log(‘3333’,res)
},function(err){
console.log(err)
})

10、生命周期钩子

一:组件创建前后:1.beforeCreate2.created 例子:data(){
return {
a:1
},
beforeCreate(){
console.log(this.a)//undefined
},
created(){
console.log(this.a)//1
}
} 二. vue 启动前后 3.beforeMount4.mounted 这两个的意思就是,vue 在 beforeMount 时,还不管事,也就是说,还没有渲染数据到 <div id=”app”><div/> 里面,此时的这个组件还是空的
当 mounted 时,才会往 <div id=”app”><div/> 添加东西,也就是 vue 正式 接管 <div id=”app”><div/>
可以获取 #app 的 innerHTML 查看差异;beforeMount(){
console.log(document.getElementById(‘app’).innerHTML)// 空的
},mounted(){
console.log(document.getElementById(‘app’).innerHTML)//#app 里的内容
} 三. 组件更新前后 5.beforeUpdate6.updated 当子组件里面的 视图改变 的时候触发。如,做一个按钮,让 data 里面的 a ++,假如 一开始 a 是 1 beforeUpdate 返回 1 updated 返回 2 例子:点击一次之后返回值分别是 1,2<button id=”button1″ @click =”handleClick”>{{a}}</button>data () { return {
a:1,
}},beforeUpdate(){ console.log(‘beforeUpdate’,document.getElementById(‘button1’).innerHTML) //1},updated(){ console.log(‘updated’,document.getElementById(‘button1’).innerHTML) //2},methods: {handleClick(){
this.a = this.a + 1;
console.log(‘ 嘿嘿 ’, this.a)
}} 四. 组件销毁前后 7.beforeDestroy8.destroyed 一个问题,如果我们在子组件里写一个定时器,然后,子组件被销毁了,定时器还会执行吗?答案是会的 所以这时候就会用到了 destroyed, 在组件被销毁后,我们把定时器给清除就好了。所以这两个钩子函数一般用于做性能的优化。六. 当捕获一个来自子孙组件的错误时被调用 11.errorCapture 当子孙组件报错的时候,父组件会触发这个钩子函数,并且会返回三个参数,第一个参数是 错误对象 第二个参数是 报错的子孙组件 第三个参数是 报错的子孙组件的具体哪个地方报错。(如,假如我没有定义 b 这个变量,但是我去 console.log(b) 这一句肯定会报错,假如我把这句错误代码写在了 created 这个钩子函数里,那第三个参数会返回就是:created hook)。
11、computed 属性和 watch 属性:

https://blog.csdn.net/joseydo…https://www.w3cplus.com/vue/v…computed 计算属性可用于快速计算视图(View)中显示的属性。这些计算将被缓存,并且只在需要时更新。我们还可以使用计算属性根据数据模型中的值或一组值来计算显示值。例子:<div v-for=”subject in results”>
<input v-model=”subject.marks”>
<span>Marks for {{subject.name}}: {{subject.marks}}</span>
</div>
<div> 学科总分 Total marks are: {{totalMarks}} </div>results: [
{name: ‘English’, marks: 70},
{name: ‘Math’, marks: 80},
{name: ‘History’, marks: 90}
]
computed: {
totalMarks: function () {
let total = 0;
let me = this;
for (let i = 0; i < me.results.length; i++)
{
total += parseInt(me.results[i].marks);
}
return total;
}
},
我们可以使用 Vue 中的 method 计算出学科的总分,最终得到的总数结果是相同的我们把 computed 区块中的 totalMarks 函数整体移到 methods 中。同时在模板中将 {{totalMarks}} 替换成 {{totalMarks() }}。你最终看到的结果是一样的。
computed 属性,和在 methods 中定义一个相同的函数都可以实现这个功能,那么区别是?
computed 属性会基于它所依赖的数据进行缓存。每个 computed 属性,只有在它所依赖的数据发生变化时,才会重新取值 (re-evaluate)。
这就意味着,只要 message 没有发生变化,多次访问 computed 属性 reversedMessage,将会立刻返回之前计算过的结果,而不必每次都重新执行函数。其次,计算属性具有缓存,相比 Vue 中的方法而言,性能更佳。但 Vue 中的计算属性都是同步的,如果需要异步我们得依赖于 vue-async-computed 插件 12、组建基础:

正文完
 0