浅谈vue3.0

vue3.0的六大亮点

  • Performance:性能比vue2.x块1.2倍到2.2倍
  • Tree shaking support:按需编译,体积比vue2.x还小
  • Composition API:组合API(类似于React中的 Hook )
  • Better TypeScript Support:更好的支持Ts
  • Custom Renderer API:暴露了自定义的渲染API
  • Fragment,Teleport(Prptal),Suspense:更先进的组件

优化了diff算法

  • vue2.0对于虚拟DOM是进行全量比较的
    • 所谓全量比较:就是对每一个标签进行前后比较
  • vue3.0新增了静态标记(PatchFlag)
    • 会根据DOM节点是否发生变化,添加静态标记 -> {{ }} 类似这种,给添加标记!
    • 然后对这些静态标记进行比较
  • 怎么添加静态标记?

标记的类型

  • text = 1 -> 动态文本
  • class // 2 -> 动态class
  • style // 4 动态style
  • props // 8 动态属性,但不包含类名和样式
  • FULL_PROPS // 16 具有动态的key属性

静态提升

  • vue2.x无论元素是否参与更新,每次都会重新创建,然后渲染
  • vue3.0中对于不参与更新的元素,会做静态提升,之后被创建一次,在渲染的时候直接复用即可
<div>
  Hello World!
  <p>{{ msg }}</p>
</div>

//静态提升之前
export function render(_ctx, _cache, $props, $setup, $data, $options) {
  return (_openBlock(), _createBlock("div", null, [
    _createTextVNode(" Hello World! "),
    _createVNode("p", null, _toDisplayString(_ctx.msg), 1 /* TEXT */)
  ]))
}
//静态提升之后 勾选 -> hoistStatic
const _hoisted_1 = /*#__PURE__*/_createTextVNode(" Hello World! ")

export function render(_ctx, _cache, $props, $setup, $data, $options) {
  return (_openBlock(), _createBlock("div", null, [
    _hoisted_1,
    _createVNode("p", null, _toDisplayString(_ctx.msg), 1 /* TEXT */)
  ]))
}

//注意点:静态提升之后,把这个变量 Hello World! 存放在全局之中,只创建一次,然后每一次渲染都是拿全局的!

事件监听缓存

  • 默认情况下onClick会被视为动态绑定,所以每次都会缓存起来复用即可
  • 但是因为是同一个函数,所以没有追踪它的变化
<div>
  <button @click="add">按钮</button>
</div>

//事件监听缓存之前
export function render(_ctx, _cache, $props, $setup, $data, $options) {
  return (_openBlock(), _createBlock("div", null, [
    _createVNode("button", { onClick: _ctx.add }, "按钮", 8 /* PROPS */, ["onClick"])
  ]))
}

//事件监听缓存之后 勾选-> cacheHandlers
export function render(_ctx, _cache, $props, $setup, $data, $options) {
  return (_openBlock(), _createBlock("div", null, [
    _createVNode("button", {
      onClick: _cache[1] || (_cache[1] = (...args) => (_ctx.add(...args)))
    }, "按钮")
  ]))
}
//事件监听缓存之后,静态标记取消,因此不再追踪,提高了性能!

vue3.0项目的创建

  • Vite || Vue-Cli || Webpack

  • 什么是Vite?

    • Vite是Vue作者开发的一款意图取代webpack的一款工具
    • 原理:使用ES6的import会发送请求去加载文件的特性
    • 拦截这些请求,做一些编译,省去冗长的webpack的打包时间
  • 安装:

    • npm i -g create-vite-app
  • 使用:

    • npm i 安装依赖
    • npm run dev 启动项目

Vue3.0是兼容vue2.x版本的

  • 依旧可以使用 data数据中的绑定方式
    • vue2.x版本的增删
<template>
  <div>
    <p>{{msg}}</p>
    <input type="text" v-model="stuObj.id" />
    <input type="text" v-model="stuObj.name" />
    <input type="submit" value="提交" @click="submit" />
    <ul>
      <li v-for="item in stu" :key="item.id" @click="removeItem(item.id)">{{ item.name }}</li>
    </ul>
  </div>
</template>

<script>
export default {
  name: "App",
  data() {
    return {
      msg: "ppp",
      stuObj: {
        id: "",
        name: "",
      },
      stu: [
        {
          id: 1,
          name: "ppp",
        },
        {
          id: 2,
          name: "www",
        },
        {
          id: 3,
          name: "qqq",
        },
      ],
    };
  },
  methods: {
    removeItem(id) {
      this.stu = this.stu.filter((item) => item.id !== id);
    },
    submit() {
      const stu = [...this.stu];
      stu.push({
        id: this.stuObj.id,
        name: this.stuObj.name,
      });
      this.stu = stu;
      this.stuObj.id = "";
      this.stuObj.name = "";
    },
  },
};
</script>

组合逻辑API

  • setup() 是组合API的入口函数
    • 这个入口函数内,定义的变量或者方法都需要向外暴露出去,使用return {}
    • 需要引入{ref} 对变量的定义初始值
<template>
  <div>
    <p>{{ count}}</p>
    <button @click="add">+1按钮</button>
  </div>
</template>

<script>
  import {
    ref
  } from "vue";
  export default {
    name: "App",
    setup() {
      //定义一个变量 count 初始值为0
      let count = ref(0);

      function add() {
        count.value++
      }

      return {
        count,
        add
      }
    }
  };
</script>
  • ref函数的注意点

    • ref函数只能监听简单数据类型的变化,不能监听复杂数据类型的变化
  • reactive函数

    • 用于监听复杂数据类型的变化

组合API的使用

<template>
  <div>
    <ul>
      <li v-for="item in state.stu" :key="item.id" @click="removeItem(item.id)">{{ item.name }}</li>
    </ul>
  </div>
</template>

<script>
  import {
    reactive
  } from "vue";
  export default {
    name: "App",
    setup() {
      let {
        state,
        removeItem
      } = useRemove()

      return {
        state,
        removeItem
      }
    },
  };

  //关于 用户删除的 数据 与 逻辑 组合api 
  //使用的时候 在setup之中直接结构出 这边方法的导入的数据
  function useRemove() {
    let state = reactive({
      stu: [{
          id: 1,
          name: "ppp",
        },
        {
          id: 2,
          name: "www",
        },
      ]
    })

    function removeItem(id) {
      state.stu = state.stu.filter(item => item.id !== id)
    }
    return {
      state,
      removeItem
    }
  }
</script>

添加用户

  • 注意点:就是关于 用户添加 用户删除的话 可以这这两个抽离出来 然后使用 import方式导入,使得每一个功能为一个单独的模块!
<template>
  <div>
    <input type="text" v-model="stuObj.id" />
    <input type="text" v-model="stuObj.name" />
    <input type="submit" value="提交" @click="addItem" />
    <ul>
      <li v-for="item in state.stu" :key="item.id" @click="removeItem(item.id)">{{ item.name }}</li>
    </ul>
  </div>
</template>

<script>
  import {
    reactive
  } from "vue";
  export default {
    name: "App",
    setup() {
      let {
        state,
        removeItem
      } = useRemove()

      let {
        stuObj,
        addItem,
      } = useAdd(state)

      return {
        state,
        removeItem,
        stuObj,
        addItem
      }
    },
  };

  //关于 用户删除的 数据 与 逻辑 组合api 
  //使用的时候 在setup之中直接结构出 这边方法的导入的数据
  function useRemove() {
    let state = reactive({
      stu: [{
          id: 1,
          name: "ppp",
        },
        {
          id: 2,
          name: "www",
        },
      ]
    })

    function removeItem(id) {
      state.stu = state.stu.filter(item => item.id !== id)
    }
    return {
      state,
      removeItem
    }
  }

  //添加
  function useAdd(state) {
    let stuObj = reactive({
      id: "",
      name: ""
    })

    function addItem() {
      state.stu.push({
        id: this.stuObj.id,
        name: this.stuObj.name
      })
      this.stuObj.id = "";
      this.stuObj.name = ""
    }

    return {
      stuObj,
      addItem,
    }
  }
</script>

setup的注意点

  • 就是setup() 函数是介于beforeCreate() 之前 此时还没有初始化好数据 无法拿到vue实例中的methods data
  • 但是vue为了避免我们错误使用,他直接把this修改为underfine
  • setup()只能是同步的,不能是异步的!

reactive方法

  • 作用:实现复杂数据的响应式方法
    • 而在vue2.x版本之中使用的是,Object.defineProperty()数据的劫持
    • 而vue3.0之中使用的proxy()来实现的
  • 注意点:
    • reactive的参数必须为一个对象(json/arr)
    • 如果给reactive传递了其他对象
      • 默认情况下修改对象,界面不会自动刷新
      • 如果想更新,可以通过重新赋值的方式
<template>
  <div>
    <p>{{state.count }}</p>
    <button @click="add">+1按钮</button>
    <p>{{ state.time }}</p>
    <button @click="addTime">时间按钮</button>
  </div>
</template>

<script>
  import {
    reactive
  } from "vue";
  export default {
    name: "App",
    setup() {
      //创建一个响应式数据
      //传入一个对象,这个对象转化为proxy对象

      // let state = reactive(123)//由于传递是 非对象形式,无法作为响应式数据 因此 add() 触发的时候,不会修改视图
      let state = reactive({
        count: 1,
        time: new Date()
      })

      function add() {
        state.count++
      }

      function addTime() {
        //传入了 其他对象 非 json arr 的 这需重新给这个数据赋值,否则不是响应式数据
        let newTime = new Date();
        newTime.setDate(state.time.getDate() + 1)
        state.time = newTime
      }
      return {
        state,
        add,
        addTime
      }
    }
  }
</script>

什么是ref函数

  • 作用:就是把简单数据类型转化为响应式数据
  • 本质:其实还是ref函数传递一个值之后,ref函数底层会自动将ref转化成reactive
    • ref(18) -> reacyive({ value:18 })
    • 因此在修改的时候,需要使用 xxx.value = XXX
    • 在template中使用的使用的时候,不需要使用.value 直接使用 xxx即可
<template>
  <div>
    <p>{{age }}</p>
    <button @click="changeAge">按钮</button>
  </div>
</template>

<script>
  import {
    ref
  } from "vue";
  export default {
    name: "App",
    setup() {
      let age = ref(1)

      function changeAge() {
        age.value = 123
      }
      return {
        age,
        changeAge
      }
    }
  }
</script>

ref 和 reactive 的区别

  • 如果在template之中使用ref数据的时候,会自动帮我们添加.value

  • 如果在template之中使用reactive数据时候,不会自动帮我们添加.value

  • Vue是如何决定是否添加.value的呢?

    • Vue在解析数据之前,会先判断这个数据是否属于ref类型,是的话就添加.value,不是的话就不添加
  • Vue怎么判断数据是ref类型?

    • 是通过当前数据的__V_ref来判断的
    • 如果有这个私有属性,并且取值为true,那么判断为ref类型,就添加.value
    • 自我判断 -> isRef isReactive 可以判断类型
      • isRef(XXX)
      • isReactive(XXX)

递归监听

  • 1:递归监听
    • 默认情况下,不管是ref还是reactive都是递归监听
  • 2:递归监听存在的问题
    • 如果数据量比较大,非常消耗性能
      • 因为每一次都是包装为proxy对象的形式
        递归监听
<template>
  <div>
    <p>{{state.gf.a }}</p>
    <p>{{state.gf.f.b }}</p>
    <p>{{state.gf.f.s.c  }}</p>
    <button @click="changeBtn">按钮</button>
  </div>
</template>

<script>
  import {
    reactive
  } from "vue";
  export default {
    name: "App",
    setup() {
      let state = reactive({
        gf: {
          a: "a",
          f: {
            b: "b",
            s: {
              c: "c"
            }
          }
        }
      })

      function changeBtn() {
        state.gf.a = "1"; //  state.value.gf.a = "1";
        state.gf.f.b = "2";
        state.gf.f.s.c = "3";
      }
      return {
        state,
        changeBtn
      }
    }
  }
</script>
  • 3:非递归监听
    • 只能监听第一层数据,不能监听内层的数据
<template>
  <div>
    <p>{{state.z }}</p>
    <p>{{state.gf.a }}</p>
    <p>{{state.gf.f.b }}</p>
    <p>{{state.gf.f.s.c  }}</p>
    <button @click="changeBtn">按钮</button>
  </div>
</template>

<script>
  import {
    // reactive
    // shallowReactive
    shallowRef,
    triggerRef
  } from "vue";
  export default {
    name: "App",
    setup() {
      // let state = reactive({
      // let state = shallowReactive({
      let state = shallowRef({
        z: "z",
        gf: {
          a: "a",
          f: {
            b: "b",
            s: {
              c: "c"
            }
          }
        }
      })

      function changeBtn() {
        // state.z = 0 // shallowReactive  非递归方式 监听  只有最外层不修改,那么后面就算数据修改了,监听不了,更新不了视图
        // state.gf.a = "1"; // 这是ref方式修改值  state.value.gf.a = "1";
        // state.gf.f.b = "2";
        // state.gf.f.s.c = "3";

        // console.log(state.gf);

        // shallowRef
        //注意点:如果是通过shallowRef创建的数据
        //那么Vue监听的是 .value的变化,并不是第一层的变化 因此不会修改数据
        // state.value.z = 0
        // state.value.gf.a = "1";
        // state.value.gf.f.b = "2";
        // state.value.gf.f.s.c = "3";

        // 监听 .value数据的变化,因此可以修改state数据 更新视图
        // state.value = {
        //   z: "0",
        //   gf: {
        //     a: "1",
        //     f: {
        //       b: "2",
        //       s: {
        //         c: "3"
        //       }
        //     }
        //   }
        // }

        //triggerRef() 主动修改 第几层的数据后 主动调用数据 修改数据并且更新视图
        state.value.gf.f.s.c = "33";
        triggerRef(state)

        console.log(state);
        console.log(state.value.gf);
      }
      return {
        state,
        changeBtn
      }
    }
  }
</script>

toRaw函数

  • 作用:拿到原始数据,对原始数据进行修改
    • 这样的话就不会被追踪了,也不会更新UI界面,使得性能更好!
<template>
  <div>
  </div>
</template>

<script>
  import {
    ref,
    toRaw,
    reactive
  } from "vue";
  export default {
    name: "App",
    setup() {
      let obj = {
        name: "ppp",
        age: 12
      } //原始数据

      let state = ref(obj) //响应式数据
      let obj2 = toRaw(state) //获取到响应式数据的原始数据
      console.log(obj === state);
      console.log(obj); //原始数据  {name: "ppp", age: 12 }
      console.log(state); //响应式数据 RefImpl {_rawValue: {…}, _shallow: false, __v_isRef: true, _value: Proxy}
      // 注意点:就是使用 toRaw拿到 ref类型的原始数据(创建时传入的那个数据) 那么需要告诉toRaw这个要拿的值是 .value
      console.log(obj2.value); //从响应式数据中拿到 原始数据 {name: "ppp", age: 12 }

      return {
        state,
      }
    }
  }
</script>

markRaw

  • 作用:就是实现数据不被追踪,数据修改后,但是界面不刷新!
<template>
  <div>
    <p>{{state.name }}</p>
    <button @click="change">按钮</button>
  </div>
</template>

<script>
  import {
    reactive,
    markRaw
  } from "vue";
  export default {
    name: "App",
    setup() {
      let obj = {
        name: "ppp",
        age: 12
      } //原始数据

      //设置 obj 这个数据 不被追踪
      obj = markRaw(obj)
      let state = reactive(obj)

      function change() {
        state.name = "lll"
        console.log(state); //{name: "lll", age: 12, __v_skip: true}  但是由于设置数据不追踪,界面不刷新 还是之前的 ppp
      }

      return {
        state,
        change
      }
    }
  }
</script>

toRef函数

  • 作用:用来创建响应式数据的
<template>
  <div>
    <p>{{state }}</p>
    <button @click="change">按钮</button>
  </div>
</template>

<script>
  import {
    ref,
    reactive,
    toRef
  } from "vue";
  export default {
    name: "App",
    setup() {
      let obj = {
        name: "ppp",
        age: 12
      } //原始数据

      //把name属性 设置为响应式数据
      // let state = ref(obj.name)
      //使用toRef 修改为响应式数据
      let state = toRef(obj, 'name')


      function change() {
        //结论:如果使用ref将一个对象中的属性变成响应式的数据 我们修改响应式的数据是不会影响到原始数据的!
        // state.value = "lll" //点击后 界面更新为 lll
        // console.log(obj); //{name: "ppp", age: 12}
        // console.log(state); //RefImpl {_rawValue: "lll", _shallow: false, __v_isRef: true, _value: "lll"}

        //结论:使用toRef方式来设置响应式数据,那么我们修改的响应式数据是会影响原始数据的 但是不会更新UI
        //应用场景:就是修改响应式数据之后,不让UI发送变化,那么就使用toRaw了
        state.value = "lll" //点击后 界面更新为 lll
        console.log(obj); //{name: "lll", age: 12}
        console.log(state); //{ ...  _object: {name: "lll", age: 12} }
      }

      return {
        state,
        change
      }
    }
  }
</script>

toRefs

  • 作用:把一个对象中的多个属性设置为响应式属性
<template>
  <div>
    <p>{{state }}</p>
    <button @click="change">按钮</button>
  </div>
</template>

<script>
  import {
    ref,
    reactive,
    toRefs
  } from "vue";
  export default {
    name: "App",
    setup() {
      let obj = {
        name: "ppp",
        age: 12
      } //原始数据

      // 把obj中的设置为响应式属性
      let state = toRefs(obj)

      function change() {
        state.value = "lll" //点击后 界面更新为 lll
        state.age = 30
        console.log(obj); //{name: "ppp", age: 12}
        console.log(state); // {name: ObjectRefImpl, age: 30, value: "lll"}
      }

      return {
        state,
        change
      }
    }
  }
</script>

自定义一个ref

<template>
  <div>
    <p>{{age }}</p>
    <button @click="change">按钮</button>
  </div>
</template>

<script>
  import {
    customRef,
    ref,
  } from "vue";
  export default {
    name: "App",
    setup() {
      function myRef(value) {
        return customRef((track, trigger) => {
          return {
            get() {
              track() // 告诉Vue数据是需要追踪的
              return value
            },
            set(newValue) {
              trigger() //告诉Vue触发界面更新
              value = newValue
            }
          }
        })
      }
      let age = myRef(18);

      function change() {
        age.value++
      }

      return {
        age,
        change
      }
    }
  }
</script>

ref获取页面的元素

<template>
  <div>
    <div ref="box">我是</div>
  </div>
</template>

<script>
  import {
    ref,
    onMounted
  } from "vue";
  export default {
    name: "App",
    setup() {
      let box = ref()

      //需要在生命周期中的  onMounted 拿到
      onMounted(() => {
        console.log("onMounted", box.value); // onMounted <div ref="box">我是</div>
      })

      return {
        box,
      }
    }
  }
</script>
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值