vue基础逻辑梳理总结

166 阅读7分钟

简单了解MVVM 的了解

  • MVC ( model view controller )的工作原理

    view : Html + Css 数据渲染层 ---> controller --->model

    controller : js 控制逻辑的 --->model

    Model :data,底层数据 ----> view

  • MVVM(Model-View-ViewModel)的工作原理

    1.数据会绑定在viewModel层并自动将数据渲染到页面中

    2.视图的变化,会通知viewModel更新数据

vue模版 {{}}

  • 问题1: 绑定数据的计算可以写到{{}}吗? 可
  • 问题2: {{}} 可以处理表达式吗? 可
  • 问题3:{{add(number)}} 可以处理吗? 可
  • 问题4:{{a>3?'ff':'dd'}}条件判断可以吗? 可
  • 问题5: {{&& || !}} 可

总结:

  • 利用花括号,构筑了数据和视图的双向绑定

  • 通过视图绑定事件,来处理数据

Vue生命周期

  • 前端需要维护一些状态,就要考虑时序的问题。生命周期也就是对时序的实现。
// 创建
 beforeCreate => created => beforeMount => mounted => beforeUpdate => updated => beforeDesrtoy => destroyed
 bC:new Vue() 空实例          ---> 实例挂在功能
 c:有实例data、props、methods  --->  数据操作、不涉及vdom和dom
 bM: vDom                    ---> 数据操作,但是不可操作dom
 m: Dom                      ----> 任何操作
 bU:vDom                     ----> vDom更新了的,dom未更新,可以操作数据
 up:dom已经更新了             -----> 谨慎操作数据
 bD:实例vm尚未被销毁。         -----> 清空eventBus reset store clear计时器
 d:实例已经被销毁              -----> 收尾

为什么vue要用一个函数data ,return 返回出去

作用域问题:如果是一个对象,对象是一个指针,当多个同名组件一起使用,同名对象会存在引用关系,也就是会发生合并的情况,当改了一个组件,其他组件也会发生改变,就做不到组件之间相互隔离了。设计成一个函数,函数内部有自己的作用域,可以隔离开,函数不会发生合并。

总结:防止组件之间数据相互污染

定向监听

computed

计算属性是基于它们的响应依赖关系缓存的。计算属性只在相关响应式依赖发生改变时它们才会重新求值。 函数会反复执行,因此不推荐

  // 计算属性
 computed:{
      changeComputed() {
      return this.test.length >0?'字符串修改':"字符串未修改"
      }
  },
// 使用
 {{ changeComputed }}

 // 方法定义
  changeComput() {
     return this.test.length >0?'字符串修改':"字符串未修改"
  }
 // 方法使用,不推荐
{{ changeComput() }}

watch

只能监听data 里面的数据,做一些处理,不要再次修改监测的值,会触发死循环

   watch:{
       test(old,newD) {
       console.log('old',old,newD)
       }
     },

computed 和 watch的区别

相同点:

  • 1.基于vue的依赖收集机制
  • 2.都是被依赖的变化触发,进而进行改变进行处理计算

不同点:

1.入和出

  • computed : 多入单出 ---- 多个值变化,组成一个值的变化
  • watch:单入多出 单个值的变化,进而影响一系列的状态变更

2.性能

  • computed:会自动diff依赖,若依赖没有变化,会从缓存中读取当前计算值
  • watch:无论监听值变化与否,都会执行回调

3.写法上

  • computed :必须有return返回值
  • watch:不一定

4.时机上

  • computed: 从首次生成赋值,就开始计算了
  • watch:首次不会运行,除非immediate:true

条件判断

v-if v-else v-else-if

  • dom上展示元素或者销毁元素
  • 可以使用在template 上,元素上
  • v-if 不要与v-for 一起使用,v-for比v-if 有更高的优先级
     <div v-if="isActive">
          测试是否是条件渲染
      </div>
      <div v-else>
          测试条件不渲染
      </div>

v-show

  • 一开始就渲染元素,dom元素始终存在文档中,只是display:none or block
  • 不能使用在template 上
    <div v-show="isActive">
      测试是否是条件渲染
  </div>
  this.isActive = !this.isActive;

v-show 对比 v-if

  • 渲染
    • v-if 无dom,不会渲染实际节点及其子节点
    • v-show 存在实际节点及其子节点,但不展示,不占据位置
  • 性能
    • v-show 初始开销比较大,适用于频繁的逻辑切换
    • v-if 切换性能开销大,条件内的元素会适当的进行销毁和删除

列表循环

  • in of 遍历都可以
  • 两个参数(item,index) , 三个参数(value,name,index)
  • 如果是修改数据,不想修改元始数据,可以使用计算属性
  • 计算属性不适用的情况下,可以使用方法去处理
  • v-for 可以使用在template,dom元素上
  <div
    v-for="(i,index) in testData"  testData =[],{} 可以是数组也可以是对象
    :key="index"
  >
    {{ i.name }}
  </div>

v-for 和 v-if 的优先级

v-for > v-if 先循环 再判断

key的作用

1.模版的编译原理 template => dom

template => 正则匹配语法。生成ST: 静态值 + 动态值 => 转换为AST 为可执行方法 => render() => dom

2.dom diff

层级:只考虑单层复用,多层遍历实现

顺序:双向指针,首位指针向中间移动

替换:移动、新增、删除;优先复用 ---key(快速识别顺序)

3.key作用 尽可能复用节点 常见问题:index做key,随机数做key是不合适的

指令

默认指令

  • v-once - 渲染一次
  • v-text - 渲染字符串
  • v-html 渲染html
  • v-bind 绑定赋值
  • v-on 监听
  • v-model 双向绑定
  • 自定义指令
---------------------v-html-----------------------
    <div>{{ testHtml }}</div> // 文本
    <div v-html="testHtml" /> //html
     data(){
        return {
        test:'sdfas',
        testHtml:'<span style="color:red">测试span</span>'
        }
    }
    
---------------------v-bind-----------------------  
 作用:表达式的值改变,响应式的作用给Dom
   <div :id="testId"> // 绑定id
        safsa
    </div>
    data(){
    return {
      test:'sdfas',
      testHtml:'<span style="color:red">测试span</span>',
      testId:'ddd'
    }
    <button :disabled='dis'>按钮</button> // 绑定禁用
    <div :[daymic]='doSome'>动态参数</div> // 动态绑定
    :class="[isActive,isColor]"  // 数组绑定样式
     <div :class="classObj"></div> //对象绑定样式
        data:{
            classObj:{
                active:true,
                textcolor:false
            }
        }
   :style="{color:activeColor}" // 内联样式
   
   ---------------------v-on-----------------------  
    <div @click='fun'></div> // 单函数
    <div @click='fun("sfs")'></div> // 单函数传递参数

    <div @click="oneFun($event),twoFun($event)"> // 多函数执行
    点击
    </div>
    ---------------------v-model-----------------------  
      表单数据绑定
     <select v-model='selected'>
      <options v-for='item in data' :value = "item.value">{{item.label}}</options>
     </select>
     ---------------------自定义指令-----------------------  
      directives:{ // v-focus
        focus:{
          mounted(el) {
            el.focus();
           
          },
         
        },
         chil:{ // v-chil
            mounted(el) {
            el.innerHTML = '添加子组件'
            }
          }
      },
      1. 可以定义全局或者局部的自定义指令
      2. 可以定义指令钩子,也就是生命周期
      3.可以动态参数

事件设计 - 为何vue把事件写在模版上,而不在js中

模版定位触发源 + 出发源区寻找触发事件逻辑 ---更方便定位问题

js与事件本身解耦,自动解绑事件

组件化

一般组件

一个文件就是一个组件,组件注册如下

components: {
   ComA,
   ComB
}

动态组件

      comTab:['World','Home2'],  // 组件名字
      currentTab:'World'  // 当前激活的组件

        <button
            v-for="tab in comTab"
            :key="tab"
            @click="handleClick(tab)"  // 切换组件
        >
            {{ tab }}
        </button>

        <component :is="currentTab" /> // 自动匹配,通过组件名字
        <keep-alive>  // 将被缓存起来
           <component :is="currentTab" />
        </keep-alive>

         handleClick(value) {
           this.currentTab = value;
        }

动态导入组件

        import { createApp, defineAsyncComponent } from 'vue'

        createApp({
        // ...
        components: {
            AsyncComponent: defineAsyncComponent(() =>
            import('./components/AsyncComponent.vue')
            )
        }
        })

插槽

局部模版的使用

默认插槽

  <Hello>
    <p>sdfas</p>
  </Hello>
  // hello 内部
  <slot />

具名插槽

以name 标记插槽的身份,可以控制具体的位置,从而在组件内部可以做到区分开来 ,接受父组件的数据

  <Hello>
   <template v-slot='test-slot'>{{name}}</template>
  </Hello>
  // Hello 内部
  <slot name='test-slot'></slot>

作用域插槽

可以接受props(scope-slot),父组件接受子组件的数据

   <Hello>
     <template v-slot='test' slot-scop="{slotProps}">{{slotProps}}</template> 
   </Hello>
   <Hello>
     <template v-slot:slotProps>{{slotProps}}</template> 
   </Hello>
   // Hello
   <slot name='test' :slotProps="data"></slot>

模版数据的二次加工

过滤器

vue2中可以这样用,vue3中已经不推荐这样用了

   // 使用 
   {{usrname | changeName }}

   //定义
   filters:{
     changeName(name) {
        // filter 过滤器this不指向实例
        return name + '?ddd';
     }
   }

通过指令 v-html

   <p v-html='name > 99 ? 99: name'></p>

jsx

vue2可以这样用,vue3已经不建议了

   render(h) { // 和react差不多
     return (
       <div>
        dasf
       </div>
     )
   }

组件化

混入 mixin

  • 1.应用:封装相同逻辑,逻辑相同,模版不一样
   mixins:[minDemo]

合并策略: data冲突时,以组件主体优先; 生命周期钩子,先mixin 执行后主体执行 递归合并,递归优先级仍以主体优先

缺点:数据来源不太明确

继承扩展 extends

应用扩展逻辑的

   extends:demo1

合并策略: 与mixin相同

合并优先级: mixin > extends 回调优先级:extends > mixin

extend

从预定义的配置中拓展一个独立配置项,并且进行合并

   let _baseOptions = {
     data:function() {
       return {
         course:'sdaf'
       }
     },
     created() {

     }
   }

   const BaseCom  = Vue.extend(_baseOptions);
   new BaseCom({
     created() {
       console.log('extend created')
     }
   })

插件

注册外部的插件

  export default {
      install:(Vue,options) => {
          Vue.globalMethod = function() {
              console.log('---')
          }
          Vue.directive('my-directive',{
              
          })
          Vue.mixin({
              created() {

              }
          })
          Vue.prototype.$yy = function() {
              
          }
      }
  }

vue 通信

  • 父组件通过props向子组件传递
  • 子组件通过emit向父级传递,父级通过方法接收
  • provide inject
  • vuex

vuex

vuex的工作原理

component 会触发action ,action会触发mutation,mutation会更新states,states再作用于component image.png

问: 为什么不直接mutation,而还要通过action操作

  • 保证states的更新已经是同步的了,数据流转就是可控的。
  • action的作用是处理所有的行为导致的值改变(async改变)
  • mutation 不应该承载太多的业务逻辑,只改变值

问:为什么vuex自己定义告警

  • 告警级别控制,可以提示,可以阻断,可以警告,可以上报

问:vuex中为什么通过Object.create(null)定义空对象,而不是{} Object.create(null).proto 是undefined,不存在原型上的一些方法,比较纯粹 ({}).proto 是Object.prototype,存在一些对象原型上的方法

vuex的使用

modules 为了隔离相互store的相互影响

   // main.js
   const app =  createApp(App)
   app.use(store)
   app.mount('#app');
   
  // store.js
  import { createStore } from "vuex";
  import testaStore from './testaSote';

   export default createStore({
       modules:{
           testaStore
       }

   })
  // testaStore.js
  export default {
   namespaced: true,
   state: {
       count: 0
     },
     mutations: {
       incre (state) {
         state.count++
       }
     },
     actions: {
       increment (context) {
         context.commit('incre')
       }
     }
 }
 
 // 计算属性使用
   computed:mapState({
   te : state => state.testaStore.count
 }),
// 方法调用
this.$store.dispatch('testaStore/increment')

SSR

  • csr static 文件 + data -----> 客户端 ----> page
  • SSR static 文件 + data ---> server ----> page ---->客户端 (提升首屏速度) SSR的优点
  1. 因为服务端渲染每次返回的只有一个页面,是一个多页应用,所以速度能够提升 spa单页应用是加载整个应用,所以速度会比较慢
  2. 利于SEO(搜索引擎检索),因为服务端刚开始就把页面信息返回了,而spa在解析出来之前是不知道的

SSR的缺点

1.spa的页面采用的是分布式的逻辑, 页面性能加载由自己的机器的性能负责

2.SSR的每次渲染都需要经过服务端处理,对所有的用户负责,压力大