Vue学习总结

本人工作两年后,工作中写的,笔记一直记录在语雀中

生命周期

普通生命周期

beforeCreate(创建前): 在此生命周期函数执行的时候,data和methods中的数据都还没有初始化。
created(创建后): 在此生命周期函数中,data和methods都已经被初始化好了,如果要调用 methods中的方法,或者操作data中的数 据,最早只能在created中操作。
beforeMount(载入前): 在此生命周期函数执行的时候,模板已经在内存中编译好了,但是尚未挂载到页面中去,此时页面还是旧的。
mounted(载入后): 此时页面和内存中都是最新的数据,这个钩子函数是最早可以操作dom节点的方法。
beforeUpdate(更新前): 此时页面中显示的数据还是旧的,但是data中的数据是最新的,且页面并未和最新的数据同步。
Updated(更新后): 此时页面显示数据和最新的data数据同步。
beforeDestroy(销毁前):当执行该生命周期函数的时候,实例身上所有的data,所有的methods以及过滤器…等都处于可用状态,并没有真正执行销毁。
destroyed(销毁后): 此时组件以及被完全销毁,实例中的所有的数据、方法、属性、过滤器…等都已经不可用了。

特殊生命周期

加入了keep-alive才会有下面这两个生命周期。

activated(组件激活时): 和上面的beforeDestroy和destroyed用法差不多,但是如果我们需要一个实例,在销毁后再次出现的话,用 beforeDestroy和destroyed的话,就太浪费性能了。实例被激活时使用,用于重复激活一个实例的时候
deactivated(组件未激活时): 实例没有被激活时。
errorCaptured(错误调用): 当捕获一个来自后代组件的错误时被调用

第一次进入keep-alive:会执行普通的生命周期函数然后再执行activated,退出执行deactivated。
第二次进入只执行:activated。

keep-alive

vue自带的组件,用来缓存组件,提升项目的性能的。减少相同入参的服务重复调用的问题。


v-if和v-show、v-for

区别

v-if:dom节点是否存在
v-show:是dom元素属性display: none/block

使用场景

v-if:初次加载和变化次数较少使用,不会加载dom盒子
v-show:频繁切换,比创建删除的开销小

v-if于v-for优先级

v-for的优先级高。
取决于源码,并没有多大意义。大概再源码一万一行左右,function genElement()

ref

获取dom,用来解决复杂繁琐的获取dom


nextTick

dom更新后,异步执行的。

简单使用场景:
再方法中用this修改了data的变量的数据,通过ref获取的innerHTML还是旧的,通过nextTick可以获取修改后的数据

一般用于服务数据和data数据冲突时 延迟计算dom数据

scoped原理

让样式只在本组件中生效,不在其他组件生效。
给节点新增自定义属性,css根据属性选择器添加样式

CSS穿透

SCSS:/deep/
stylus:>>>、/deep/

传值通讯

父子传值

父->子

props

:key='value'
props: {
  key: String(类型)
}

子->父

$emit

this.$emit(key,value)

子组件:
  this.$emit(key,value)
父组件:
  @key='getFunction'
  methods:{
    getFunction(value){
      // value就是子组件的传值
    }
  }

兄弟传值

通过中转实现(bus)

import bus from '@/common/bus'

  bus.$emit(key,value)
  bus.$on(key,(data)=>{
    // data就是传值
  })

computed、methods、watch区别
computed:计算属性 计算属性是基于它们的响应式依赖进行缓存的
methods:方法
watch:监听

computed和methods

computed:是有缓存的
methods: 没有缓存的

computed和watch

watch:数据或者路由发生改变才会执行,监听的值发生改变。
computed:可以计算某一个属性的改变,内部的一个值改变了也会监听到并返回最新的值

props和data优先级

props优先级大于data
源码中已经体现了优先级,大概在50行左右;
优先级循序:props -> methods -> data -> computed -> watch

Vuex

专为 Vue.js 应用程序开发的状态管理模式 + 库

属性

state:用于存放数据。类似于组件中的data
getters:计算属性,类似组件中的computed
mutations(同步事务):方法,类似组件中的methods
actions:提交mutatations
modules:模板,可以再细分让仓库更好管理

Action(可以异步操作):提交的是 mutation,而不是直接变更状态

Vuex是单向数据流

不能直接更改state中的数据

持久化存储

Vuex本身不是持久化存储

  1. 使用localStorage
  2. 使用插件(npm install --save vuex-persist)
export default{
  state: {
    num: localStorage.getItem('key') || 1
  },
  mutations: {
    add ( state ) {
      state.num++
      localStorage.setItem('key',state.num)
    }
}

Vue代理

vue cli中的 devServer

module.exports = {
  devServer: {
    proxy: 'http://localhost:3000'
  }
}

打包问题处理

使用publicPath打包修正

module.exports = {
  publicPath: './',
  devServer: {
    proxy: 'http://localhost:3000'
  }
}

路由

路由模式

自己写代码测试使用hash模式
上线一般使用history

区别

形态区别

hash带 # :http://localhost:8080/#/home(例)
history:http://localhost:8080/home(例)

跳转请求

history:找不到的时候会发送请求
http://localhost:8080/id(例) ===> 发送请求
hash: 不会发送请求

打包前端自测使用hash,如果使用history会出现空白页

路由传值

两种方式:显式、隐式

显式:http://localhost:8080/#/test?a=1
通过path跳转路径 query传递

this.$router.push({
  path: '/test',
  query: {
    a: 1
  }
})

// 获取
  this.$route.query.a

隐式:http://localhost:8080/#/test

this.$router.push({
  name: 'test',
  params: {
    a: 1
  }
})

// 获取
  this.$route.params.a

路由导航守卫

分类:全局路由独享组件内
全局:一般在main.js文件使用,还有在路由文件中使用
路由独享:在配置路由里面写
组件内:在组件中使用,一般不会用到

全局

一般在 main.js 文件中使用 (还有在路由文件中使用),router 即是路由。

  • next(false): 取消当前的导航。如果浏览器的 URL 改变了(可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from 路由对应的地址。就像我们调用 router.push 一样。
全局前置守卫 beforeEach
router.beforeEach((to, from, next) => {
  console.log(to) => // 到哪个页面去?
  console.log(from) => // 从哪个页面来?
  next() => // 一个回调函数,顺利通过,往下走
}
全局解析守卫 beforeResolve

确保在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被正确调用。

router.beforeResolve(async to => {
  if (to.meta.requiresCamera) {
    try {
      await askForCameraPermission()
    } catch (error) {
      if (error instanceof NotAllowedError) {
        // ... 处理错误,然后取消导航
        return false
      } else {
        // 意料之外的错误,取消导航并把错误传给全局处理器
        throw error
      }
    }
  }
})
全局后置钩子 afterEach

没有 next函数 也不会改变导航本身

router.afterEach((to, from, failure) => {
  if (!failure) sendToAnalytics(to.fullPath)
})

路由独享beforeEnter

从一个路由跳到另一个路由导航时才会触发,params、**query **或 hash 改变时不会触发
**只在路由进入是触发,写在路由文件中。**

const routes = [
  {
    path: '/users/:id',
    component: UserDetails,
    beforeEnter: (to, from) => {
      // reject the navigation
      return false
    },
  },
]

组件内(不常用)

进入路由:beforeRouteEnter
路由改变:beforeRouteUpdate
离开渲染该组件:beforeRouteLeave
必须有 next();,相当于一个按钮开启一样,使用了next 代表通过,直接进to 所指路由

  • 进入路由时调用,此时不能获取组件实例 this ,因为的当前守卫执行前,组件实例还没被创建
beforeRouteEnter(to,from,next){
  console.log(to,to);
  next(vm => {
    // 通过 `vm` 访问组件实例
  })
}
  • 在当前路由改变,但是该组件被复用时调用,它可以访问组件实例 this,举例来说,对于一个带有动态参数的路径 /users/:id,在 /users/1 和 /users/2 之间跳转的时候
beforeRouteUpdate (to, from, next) {
  console.log(to, 'to')
  next(vm => {
    // 通过 `vm` 访问组件实例
  })
}
  • 在导航离开渲染该组件的对应路由时调用(离开当前路由页面时调用),与 beforeRouteUpdate 一样,它可以访问组件实例 this
beforeRouteLeave (to, from, next) {
  console.log(to,to)
  next(vm => {
    // 通过 `vm` 访问组件实例
  })
}

动态路由 children

一般来说就是,前面一个不改变通过改变后面一个。
通过动态路由实现当前页面下,点击切换到下面内同时不刷新当前页面的方法

{
  path: '/list',
  name: 'List',
  children: [
    {
      path: '/list/:id',
      component: () => import(/* webpackChunkName: "List" */ '../views/routerList/details.vue')
    }
  ],
  component: () => import(/* webpackChunkName: "List" */ '../views/routerList/list.vue')
}

SPA以及SPA优缺点

SPA单页面应用 的意思

缺点:

  1. SEO优化不好 (多页面,动态的改变等)
  2. 性能不是特别好(造成重复、浪费啊等问题)

源码解析(知道原理就好)

index.txtvue.js

双向绑定原理

通过Object.defineProperty劫持数据发生的改变,如果数据发生改变了(在set中进行赋值的),触发update方法进行更新节点内容({{ str }}),从而实现了数据双向绑定的原理。

class Vue{
  constructor( options ){
    this.$el = document.querySelector(options.el)		// 获取节点
    this.$data = options.data		// 获取数据
    this.compile(  this.$el )		// 绑定数据方法
  }

  compile( node ){
    node.childNodes.forEach((item,index)=>{
      // 元素节点
      if( item.nodeType == 1 ){
        if( item.childNodes.length > 0  ){
					this.compile(  item )		// 递归查找内同
				}
      }
      // 这是文本节点,如果有{{}}就替换成数据
      if( item.nodeType == 3 ){
        let reg = /\{\{(.*?)\}\}/g		// 正则匹配{{}}
        let text = item.textContent		// 获取文本内容
        // 给节点赋值
        item.textContent = text.replace(reg,(match,vmKey)=>{
          vmKey = vmKey.trim()
          return this.$data[vmKey]	// 数据中的data数据
        })
      }
    })
  } 
}

生命周期

接上代码修改

constructor( options ){
		if(  typeof options.beforeCreate == 'function' ){
			options.beforeCreate.bind(this)()	
      // this.$el	  this.$data
      // undefined	undefined
		}
		// 获取data,后面的生命周期也都有了 this.$data
		this.$data = options.data
		if(  typeof options.created == 'function' ){
			options.created.bind(this)()
      // this.$el	  this.$data
      // undefined	{}		
		}
		if(  typeof options.beforeMount == 'function' ){
			options.beforeMount.bind(this)()
      // this.$el	  this.$data
      // undefined	{}		
		}
		// 获取节点,后面的生命周期也都有了 this.$el
		this.$el = document.querySelector(options.el);
		// 模版解析
		this.compile(  this.$el )
		if(  typeof options.mounted == 'function' ){
			options.mounted.bind(this)()
      // this.$el	  		this.$data
      // <div id="app"	{a: '1'}
		}
}

事件与data数据劫持

事件绑定

compile( node ){
  node.childNodes.forEach((item,index)=>{
    // 判断元素节点是否绑定了@click
    if( item.hasAttribute('@click')  ){
      // @click后绑定的属性值
      let vmKey = item.getAttribute('@click').trim()
      item.addEventListener('click',( event )=>{
        this.eventFn = this.$options.methods[vmKey].bind(this);
        this.eventFn(event)
      })
    }
    // 元素节点
    if( item.nodeType == 1 ){
      if( item.childNodes.length > 0  ){
        this.compile(  item )		// 递归查找内同
      }
    }
    // 这是文本节点,如果有{{}}就替换成数据
    if( item.nodeType == 3 ){
      let reg = /\{\{(.*?)\}\}/g		// 正则匹配{{}}
      let text = item.textContent		// 获取文本内容
      // 给节点赋值
      item.textContent = text.replace(reg,(match,vmKey)=>{
        vmKey = vmKey.trim()
        return this.$data[vmKey]	// 数据中的data数据
      })
    }
  })
} 

data数据劫持

constructor( options ){
  this.$el = document.querySelector(options.el)		// 获取节点
  this.$data = options.data		// 获取数据
  this.proxyData()	// data数据劫持
  this.compile(  this.$el )		// 绑定数据方法
}

// 1、给Vue大对象赋属性,来自于data中
// 2、data中的属性值和Vue大对象的属性保持双向(劫持)
proxyData(){
  for( let key in this.$data ){
    Object.defineProperty(this,key,{
      get(){
        return this.$data[key]
      },
      set( val ){
        this.$data[key] = val
      }
    })
  }
}
Logo

前往低代码交流专区

更多推荐