博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Vue 3.0 学习理解
阅读量:3946 次
发布时间:2019-05-24

本文共 13614 字,大约阅读时间需要 45 分钟。

Vue 3.0 学习理解

1. Vue 3.0 开篇

1.1 Vue 3.0 六大亮点

  • Performance性能比Vue 2.x1.2~2倍
  • Tree shaking support按需编译,体积比Vue2.x更小
  • Composition API: 组合API(类似React Hooks)
  • Better TypeScript support:更好的 Ts 支持
  • Custom Renderer API:暴露了自定义渲染API
  • Fragment, Teleport(Protal), Suspense:更先进的组件

1.2 Vue3.0是如何变快的

  • diff 算法优化
  • hoistStatic 静态提升
  • cacheHandlers 事件侦听器缓存
  • ssr 渲染

2. Vue 3.0 diff 算法

2.1 diff 算法优化

  • Vue2 中的虚拟dom是进行全量的对比

在这里插入图片描述

  • Vue3 新增了静态标记(PatchFlag)
    • 与上次虚拟节点进行对比时候只对比带有patch flag的节点
    • 并且可以通过 flag 的信息得知当前节点要对比的具体内容

在这里插入图片描述

张三

张三

张三

{

{
msg}}}

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

2.2 静态标记值

  • PatchFlags
export const enum PatchFlags {
TEXT = 1,// 动态文本节点 CLASS = 1 << 1, // 2 // 动态 class STYLE = 1 << 2, // 4 // 动态 style PROPS = 1 << 3, // 8 // 动态属性,但不包含类名和样式 FULL_PROPS = 1 << 4, // 16 // 具有动态 key 属性,当 key 改变时,需要进行完整的 diff 比较。 HYDRATE_EVENTS = 1 << 5, // 32 // 带有监听事件的节点 STABLE_FRAGMENT = 1 << 6, // 64 // 一个不会改变子节点顺序的 fragment KEYED_FRAGMENT = 1 << 7, // 128 // 带有 key 属性的 fragment 或部分子字节有 key UNKEYED_FRAGMENT = 1 << 8, // 256 // 子节点没有 key 的 fragment NEED_PATCH = 1 << 9, // 512 // 一个节点只会进行非 props 比较 DYNAMIC_SLOTS = 1 << 10, // 1024 // 动态 slot HOISTED = -1, // 静态节点 // 指示在 diff 过程应该要退出优化模式 BAIL = -2}

3. Vue 3.0 静态提升和监听缓存

3.1 hoistStatic 静态提升

  • Vue2 中无论元素是否参与更新, 每次都会重新创建, 然后再渲染
  • Vue3 中对于不参与更新的元素, 会做静态提升, 只会被创建一次, 在渲染时直接复用即可

张三

张三

张三

{

{
msg}}}

================================================================================静态提升之前:export function render(_ctx, _cache, $props, $setup, $data, $options) {
return (_openBlock(), _createBlock("div", null, [ _createVNode("p", null, "张三"), _createVNode("p", null, "张三"), _createVNode("p", null, "张三"), _createVNode("p", null, _toDisplayString(_ctx.msg) + "}", 1 /* TEXT */) ]))}================================================================================静态提升之后:const _hoisted_1 = /*#__PURE__*/_createVNode("p", null, "张三", -1 /* HOISTED */)const _hoisted_2 = /*#__PURE__*/_createVNode("p", null, "张三", -1 /* HOISTED */)const _hoisted_3 = /*#__PURE__*/_createVNode("p", null, "张三", -1 /* HOISTED */)export function render(_ctx, _cache, $props, $setup, $data, $options) {
return (_openBlock(), _createBlock("div", null, [ _hoisted_1, _hoisted_2, _hoisted_3, _createVNode("p", null, _toDisplayString(_ctx.msg) + "}", 1 /* TEXT */) ]))}

3.2 cacheHandlers 事件侦听器缓存

  • 默认情况下 onClick 会被视为动态绑定, 所以每次都会去追踪它的变化
  • 但是因为是同一个函数,所以没有追踪变化, 直接缓存起来复用即可
事件监听缓存
================================================================================开启事件监听缓存之前:export function render(_ctx, _cache, $props, $setup, $data, $options) {
return (_openBlock(), _createBlock("div", null, [ _createVNode("button", {
onClick: _ctx.onClick }, "按钮", 8 /* PROPS */, ["onClick"]) ]))}================================================================================开启事件监听缓存之后:export function render(_ctx, _cache, $props, $setup, $data, $options) {
return (_openBlock(), _createBlock("div", null, [ _createVNode("button", {
onClick: _cache[1] || (_cache[1] = (...args) => (_ctx.onClick(...args))) }, "按钮") ]))}

4. Vue 3.0 快速上手

4.1 创建Vue 3.0 的3种方式

  • Vue-CLI
  • Webpack
  • Vite

4.2 Vue-CLI 创建

npm install -g @vue/clivue create projectNamecd projectNamevue add vue-nextnpm run serve

4.3 Webpack 创建

git clone https://github.com/vuejs/vue-next-webpack-preview.git projectNamecd projectNamenpm installnpm run dev

4.4 Vite 创建

4.4.1 什么是 Vite
  • Vite 是 Vue 作者开发的一款意图取代 webpack 的工具
4.4.2 Vite 实现原理
  • 利用 ES6import发送请求去加载文件的特性
  • 拦截请求, 做一些预编译, 省去 webpack 冗长的打包时间
4.4.3 安装 Vite
  • npm install -g create-vite-app
4.4.4 利用 Vite 创建 Vue 3.0 项目
  • create-vite-app projectName
4.4.5 安装依赖运行项目
  • cd projectName
  • npm install
  • npm run dev

4.5 Vue 3.0 兼容 Vue 2.x

4.5.1 Vue 2.x 存在的问题
  • 数据与逻辑处理分开
  • 耗性能,维护不方便

5. Vue 3.0 组合 API

5.1 Composition API 基本用法

5.2 Composition API 抽取

5.3 Composition API 组合

  • App.vue
  • rem.js
import {
reactive} from 'vue';function useRemoveStudent() {
let state = reactive({
stus:[ {
id:1, name:'zs', age:10}, {
id:2, name:'ls', age:20}, {
id:3, name:'ww', age:30}, ] }); function remStu(index) {
state.stus = state.stus.filter((stu, idx) => idx !== index); } return {
state, remStu};}export default useRemoveStudent;
  • add.js
import {
reactive} from 'vue';function useAddStudent(state) {
let state2 = reactive({
stu:{
id:'', name:'', age:'' } }); function addStu(e) {
e.preventDefault(); const stu = Object.assign({
}, state2.stu); state.stus.push(stu); state2.stu.id = ''; state2.stu.name = ''; state2.stu.age = ''; } return {
state2, addStu}}export default useAddStudent;
  • 图解组合 API

在这里插入图片描述

5.4 Composition API 本质

  • Composition APIOption API 混合使用
  • Composition API 本质 (组合API/注入API)
    • 将暴露的数据注入到 data 中
    • 将暴露的方法注入到 methods 中

6. setup 执行时机及注意点

6.1 setup 执行时机

  • setup
  • beforeCreate: 表示组件刚刚被创建出来, 组件的data和methods还没有初始化好
  • Created : 表示组件刚刚被创建出来, 并且组件的data和methods已经初始化好

6.2 setup 注意点

  • 由于在执行 setup 函数的时候, 还没有执行 Created 生命周期方法
    • 所以在 setup 函数中,是无法使用 data 和 methods
  • 由于不能在 setup 函数中使用 data 和 methods
    • 所以 Vue 为了避免错误的使用, 它直接将 setup 函数中 this 修改成了 undefined
  • setup 函数只能是同步的不能是异步的

7. reactive 理解

7.1 什么是reactive?

  • reactive 是 Vue3 中提供的实现响应式数据的方法
  • Vue2 中响应式数据是通过 defineProperty 来实现
  • 在 Vue3 中响应式数据是通过 ES6 的 Proxy 来实现

7.2 reactive注意点

  • reactive 参数必须是对象(json/arr)
  • 如果给 reactive 传递了其它对象
    • 默认情况下修改对象, 界面不会自动更新
    • 如果想更新, 可以通过重新赋值的方式

8. ref 理解

8.1 什么是 ref

  • ref 和 reactive 一样, 也是用来实现响应式数据的方法
  • 由于 reactive 必须传递一个对象
    • 所以导致在企业开发中,如果我们只想让某个变量实现响应式的时候会非常麻烦
    • 所以 Vue3 就给我们提供了 ref 方法, 实现对简单值的监听

8.2 ref 本质

  • ref 底层的本质其实还是 reactive
  • 系统会自动根据我们给 ref 传入的值将它转换成
    • ref(xx) -> reactive({value:xx})

8.3 ref 注意点

  • 在 Vue 中使用 ref 的值不用通过 value 获取
  • 在 JS 中使用 ref 的值必须通过 value 获取

9. ref 和 reactive 区别

  • ref 和 reactive 区别

    • 如果在 template 里使用的是 ref 类型的数据, 那么 Vue 会自动帮我们添加 .value
    • 如果在 template 里使用的是 reactive 类型的数据, 那么 Vue 不会自动帮我们添加 .value
  • Vue 是如何决定是否需要自动添加 .value 的

    • Vue 在解析数据之前, 会自动判断这个数据是否是 ref 类型
    • 如果是就自动添加 .value, 如果不是就不自动添加 .value
  • Vue 是如何判断当前的数据是否是 ref 类型的

    • 通过当前数据的私有属性 __v_ref 来判断
    • 如果有这个私有的属性, 并且取值为 true, 那么就代表是一个 ref 类型的数据
  • 我们如何判断数据到底是 ref 还是 reactive

    • 通过 isRef / isReactive 方法

10. 递归监听

10.1 递归监听

  • 默认情况下, 无论是通过 ref 还是 reactive 都是递归监听

10.2 递归监听存在的问题

  • 如果数据量比较大, 非常消耗性能

11. 非递归监听

  • 如何触发非递归监听属性更新界面
    • 如果是 shallowRef 类型数据, 可以通过 triggerRef 来触发

11.1 应用场景

  • 一般情况下我们使用 ref 和 reactive 即可
  • 只有在需要监听的数据量比较大的时候, 我们才使用 shallowRef/shallowReactive

12. shallowRef 本质

13. toRaw 理解

  • toRaw

    • 从 Reactive 或 Ref 中得到原始数据
  • toRaw 作用

    • 做一些不想被监听的事情(提升性能)

14. markRaw 理解

  • 将数据标记为永远不能追踪的数据
  • 一般在编写自己的第三方库时使用

15. toRef 理解

15.1 toRef

  • 创建一个 ref 类型数据, 并和以前的数据关联

15.2 toRef 和 ref 区别

  • ref
    • 创建出来的数据和以前无关(复制)
    • 数据变化会自动更新界面
  • toRef
    • 创建出来的数据和以前的有关(引用)
    • 数据变化不会自动更新界面

16. toRefs 理解

16.1 toRefs

  • 批量创建 ref 类型数据, 并和以前数据关联

17. customRef 理解

  • customRef
    • 返回一个 ref 对象, 可以显式地控制依赖追踪和触发响应

18. ref 获取元素

18.1 获取元素

  • 在 Vue2.x 中我们可以通过给元素添加 ref='xxx'
  • 然后在代码中通过 refs.xxx 的方式来获取元素
  • 在 Vue3.0 中我们也可以通过 ref 来获取元素

19. readonly 家族

  • readonly
    • 用于创建一个只读的数据,并且是递归只读
  • shallowReadonly
    • 用于创建一个只读的数据,但是不是递归只读的
  • isReadonly
    • 判断是否是一个只读的数据
  • const 和 readonly 区别
    • const: 赋值保护不能给变量重新赋值
    • readonly: 属性保护不能给属性重新赋值

20. Vue 3.0 响应式数据本质

20.1 Vue 3.0 响应式数据本质

  • Vue 2.x 中是通过 defineProperty 来实现响应式数据的
  • Vue 3.0 中是通过 Proxy 来实现响应式数据的
let obj = {
name: 'lnj', age: 18};let state = new Proxy(obj, {
get(obj, key){
console.log(obj, key); // { name: 'lnj', age: 18 } name return obj[key]; }, set(obj, key, value){
console.log(obj, key, value ); // { name: 'lnj', age: 18 } name zx obj[key] = value; console.log('更新UI界面'); }});// console.log(state.name); // lnjstate.name = 'zx';console.log(state);

20.2 Proxy 注意点

  • set 方法必须通过返回值告诉 Proxy 此次操作是否成功
let arr = [1, 3, 5]; // [1, 3, 5, 7]let state = new Proxy(arr, {
get(obj, key){
console.log(obj, key); // [1, 3, 5 ] 1 return obj[key]; }, set(obj, key, value){
// [1, 3, 5 ] 3 7 // [1, 3, 5, 7 ] length 4 console.log(obj, key, value ); obj[key] = value; console.log('更新UI界面'); return true; }});console.log(state[1]);state.push(7);

21. 手写 shallowReactive——shallowRef 理解

  • shallowReactive
function shallowReactive(obj){
return new Proxy(obj, {
get(obj, key){
return obj[key]; }, set(obj, key, val){
obj[key] = val; console.log('更新UI界面'); return true; } })}let obj = {
a: 'a', gf:{
b:'b', f;{
c:'c', s:{
d:'d' } } }}let state = shallowReactive(obj);state.a = '1'; // 更新UI界面state.gf.b = '2';state.gf.f.c = '3';state.gf.f.s.d = '4';
  • shallowRef
function shallowRef(val){
return shallowReactive({
value:val});}function shallowReactive(obj){
return new Proxy(obj, {
get(obj, key){
return obj[key]; }, set(obj, key, val){
obj[key] = val; console.log('更新UI界面'); return true; } })}let obj = {
a: 'a', gf:{
b:'b', f;{
c:'c', s:{
d:'d' } } }}let state = shallowRef(obj);// 没效果// state.value.a = '1';// state.value.gf.b = '2';// state.value.gf.f.c = '3';// state.value.gf.f.s.d = '4';state.value = {
a:1, gf:{
b:2, f;{
c:3, s:{
d:4 } } }}

22. 手写 reactive-ref 理解

  • reactive
function reactive(obj){
if(typeof obj === 'object'){
if(obj instanceof Array){
// 如果是一个数组,那么取出数组中的每一个元素, // 判断每一个元素是否又是一个对象,如果又是一个对象,那么也需要包装成 Proxy obj.forEach((item, index) => {
if(typeof item === 'object'){
obj[index] = reactive(item); } }) }else{
// 如果是一个对象,那么取出对象属性的取值, // 判断对象属性的取值是否又是一个对象,如果又是一个对象,那么也需要包装成 Proxy for(let key in obj){
let item = obj[key]; if(typeof item === 'object'){
obj[key] = reactive(item); } } } return new Proxy(obj, {
get(obj, key){
return obj[key]; }, set(obj, key, val){
obj[key] = val; console.log('更新UI界面'); return true; } }) }else{
console.warn(`${
obj} is not object`); }}/*let obj = { a: 'a', gf:{ b:'b', f;{ c:'c', s:{ d:'d' } } }}let state = reactive(obj)state.a = 1;state.gf.b = 2;state.gf.f.c = 3;state.gf.f.s.d = 4;*/let arr = [{
id:1, name:'鲁班', attr:{
age:18}}, {
id:2, name:'虞姬'}]let state = reactive(arr)state[0].name='zx'state[0].attr.age = 666state[1].id = 3
  • ref
function ref(val){
return reactive({
value:val})}function reactive(obj){
if(typeof obj === 'object'){
if(obj instanceof Array){
// 如果是一个数组,那么取出数组中的每一个元素, // 判断每一个元素是否又是一个对象,如果又是一个对象,那么也需要包装成 Proxy obj.forEach((item, index) => {
if(typeof item === 'object'){
obj[index] = reactive(item); } }) }else{
// 如果是一个对象,那么取出对象属性的取值, // 判断对象属性的取值是否又是一个对象,如果又是一个对象,那么也需要包装成 Proxy for(let key in obj){
let item = obj[key]; if(typeof item === 'object'){
obj[key] = reactive(item); } } } return new Proxy(obj, {
get(obj, key){
return obj[key]; }, set(obj, key, val){
obj[key] = val; console.log('更新UI界面'); return true; } }) }else{
console.warn(`${
obj} is not object`); }}/*let obj = { a: 'a', gf:{ b:'b', f;{ c:'c', s:{ d:'d' } } }}let state = reactive(obj)state.a = 1;state.gf.b = 2;state.gf.f.c = 3;state.gf.f.s.d = 4;*/let arr = [{
id:1, name:'鲁班', attr:{
age:18}}, {
id:2, name:'虞姬'}]let state = reactive(arr)state[0].name='zx'state[0].attr.age = 666state[1].id = 3

23. 手写 readonly-shallowReadonly 理解

  • shallowReadonly
function shallowReadonly(obj){
return new Proxy(obj, {
get(obj, key){
return obj[key]; }, set(obj, key, val){
// obj[key] = val; // console.log('更新UI界面'); // return true; console.warn(`${
key}是只读的,不能赋值`); } })}let obj = {
a: 'a', gf:{
b:'b', f;{
c:'c', s:{
d:'d' } } }}let state = shallowReadonly(obj)state.a = 1state.gf.b = 2

转载地址:http://dcqwi.baihongyu.com/

你可能感兴趣的文章
惰性求值,可组合和模块化的JavaScript
查看>>
How to Extend Django User Model 如何扩展Django用户模型
查看>>
两个行业的故事:编程语言与富裕国家和发展中国家之间的差异
查看>>
15个用于管理MySQL服务器mysqladmin命令
查看>>
服务器端I / O性能:Node,PHP,Java与Go
查看>>
多行文本编辑时,同一行编辑不同类型的字符时自动换行的问题
查看>>
如何使开机动画只播一次
查看>>
如何在平台上实现LED灯的效果?如信号灯,来短信/来电时LED动画闪烁
查看>>
restore factory属性的enable和disable
查看>>
Android LOG机制流程图
查看>>
如何在JNI中抛异常
查看>>
Android应用程序的完全退出
查看>>
Task和Activity相关的一些属性
查看>>
JAVA系统属性之user.home
查看>>
Android代码截屏
查看>>
Android中打印代码的调用层次
查看>>
成功者十三个价值连城的习惯
查看>>
特别成功的人会做6件事
查看>>
Android: 用jni 获取MAC地址
查看>>
字符串列表的C语言实现:c_strlist
查看>>