vue3 知识点 和 对比 部分总结, 更新中。。。。。

1. node 环境 > 16

2. 给 VS code 安装 Volar 扩展插件

3. 整个应用程序的app暴露出一个全局配置选项

const app = createApp(App)

// 应用实例会暴露一个 .config 对象允许我们配置一些应用级的选项
app.config.errorHandler = (err)=> {
    console.log("程序的错误信息。。。")
    console.log(err)
}

app.mount("#app")

4. 整个应用程序的app暴露出一个全局组件注册方法

const app = createApp(App)

// 应用实例还提供了一些方法来注册应用范围内可用的资源,例如注册一个组件:
// app.component('全局组件名称', 一个全局组件)

app.mount("#app")

5. 给整个应用程序添加全局数据

// 添加全局数据
app.config.globalProperties = {
    sx: 777
}

// 接下来在组件的模板中可直接获取使用 
{{sx}}
// 在组件脚本中, 需要先获取全局实例对象
20220928095455148.png

6. 创建一个响应式的 对象 或 数组

// 注意: reactive() 返回的是一个原始对象的 Proxy
const raw = {}
const proxy = reactive(raw)
只有代理对象是响应式的,更改原始对象不会触发更新。

为保证访问代理的一致性,
对同一个原始对象调用 reactive() 会总是返回同样的代理对象,
而对一个已存在的代理对象调用 reactive() 会返回其本身:

// 在同一个对象上调用 reactive() 会返回相同的代理
console.log(reactive(raw) === proxy) // true

// 在一个代理上调用 reactive() 会返回它自己
console.log(reactive(proxy) === proxy) // true

这个规则对嵌套对象也适用。依靠深层响应性,响应式对象内的嵌套对象依然是代理:
const proxy = reactive({})

const raw = {}
proxy.nested = raw

console.log(proxy.nested === raw) // false

因为 Vue 的响应式系统是通过属性访问进行追踪的,
因此我们必须始终保持对该响应式对象的相同引用。
这意味着我们不可以随意地“替换”一个响应式对象,
因为这将导致对初始引用的响应性连接丢失:
let state = reactive({ count: 0 })
// 上面的引用 ({ count: 0 }) 将不再被追踪(响应性连接已丢失!)
state = reactive({ count: 1 })

// n 是一个局部变量,同 state.count
// 失去响应性连接
let n = state.count
// 不影响原始的 state
n++

// count 也和 state.count 失去了响应性连接
let { count } = state
// 不会影响原始的 state
count++

// 该函数接收一个普通数字,并且
// 将无法跟踪 state.count 的变化
callSomeFunction(state.count)

// 组合式 api


// 选项式 api

7. nextTick() 等 编程了 全局api

import { nextTick } from 'vue'

8. 深层响应式对象 & 浅层响应式对象

9. 响应式变量

Vue 提供了一个 ref() 方法来允许我们创建可以使用任何值类型的响应式 ref:
import { ref } from 'vue'

const count = ref(0)

ref() 将传入参数的值包装为一个带 .value 属性的 ref 对象
当值为对象类型时,会用 reactive() 自动转换它的 .value。
const objectRef = ref({ count: 0 })
// 这是响应式的替换
objectRef.value = { count: 1 }


ref 被传递给函数或是从一般对象上被解构时,不会丢失响应性
const obj = {
  foo: ref(1),
  bar: ref(2)
}

// 该函数接收一个 ref
// 需要通过 .value 取值
// 但它会保持响应性
callSomeFunction(obj.foo)

// 仍然是响应式的
const { foo, bar } = obj

简言之,ref() 让我们能创造一种对任意值的 “引用”,并能够在不丢失响应性的前提下传递这些引用。


ref的解包
ref在模板中解包
当 ref 在模板中作为顶层属性被访问时,它们会被自动“解包”,所以不需要使用 .value

  
function increment() {
  count.value++ // 需要.value
}

ref 在响应式对象中的解包
当一个 ref 被嵌套在一个响应式对象中,作为属性被访问或更改时,它会自动解包,因此会表现得和一般的属性一样

数组和集合类型的 ref 解包
跟响应式对象不同,当 ref 作为响应式数组或像 Map 这种原生集合类型的元素被访问时,不会进行解包
const books = reactive([ref('Vue 3 Guide')])
// 这里需要 .value
console.log(books[0].value)

const map = reactive(new Map([['count', ref(0)]]))
// 这里需要 .value
console.log(map.get('count').value)

10. 计算属性




computed() 方法期望接收一个 getter 函数,返回值为一个计算属性 ref。和其他一般的 ref 类似,你可以通过 xxx.value 访问计算结果。计算属性 ref 也会在模板中自动解包,因此在模板表达式中引用时无需添加 .value

11. 计算属性 & 方法

计算属性值会基于其响应式依赖被缓存

一个计算属性仅会在其响应式依赖更新时才重新计算。这意味着只要 依赖的响应式数据 不改变,无论多少次访问 计算属性 都会立即返回先前的计算结果,而不用重复执行 getter 函数。

方法调用总是会在重渲染发生时再次执行函数。

像下面的计算属性永远不会更新, 因为 Date.now() 并不是一个响应式依赖:

const now = computed(() => Date.now())

12. 计算属性为什么要缓存

想象一下我们有一个非常耗性能的计算属性 list,需要循环一个巨大的数组并做许多计算逻辑,并且可能也有其他计算属性依赖于 list。没有缓存的话,我们会重复执行非常多次 list 的计算函数,然而这实际上没有必要!如果你确定不需要缓存,那么也可以使用方法调用。

13. 可写计算属性

只在某些特殊场景中你可能才需要用到“可写”的属性,你可以通过同时提供 getter 和 setter 来创建:


现在当你再运行 fullName.value = 'John Doe' 时,setter 会被调用而 firstNamelastName 会随之更新。

另注意:

在计算属性中使用 reverse()sort() 的时候务必小心!这两个方法将变更原始数组,计算函数中不应该这么做。请在调用这些方法之前创建一个原数组的副本:

- return 数组.reverse()
+ return [...数组].reverse()

14. 数组变化侦测

变更方法

Vue 能够侦听响应式数组的变更方法,并在它们被调用时触发相关的更新。

变更: 就是改变数组内的元素, 但不会生成新的数组

  • push()
  • pop()
  • shift()
  • unshift()
  • splice()
  • sort()
  • reverse()

替换一个数组

例如 filter()concat()slice()等,这些方法都不会更改原数组,而总是返回一个新数组

在这种情况下, 我们需要用新生成的数组 来 替换 老的数组

比如 :item 是一个数组的 ref
那么: 可以这样来操作

item.value = item.value.concat(...)

其本质就是 , 保证 ref 的引用不变, 还是响应式的

你可能认为这将导致 Vue 丢弃现有的 DOM 并重新渲染整个列表——幸运的是,情况并非如此。Vue 实现了一些巧妙的方法来最大化对 DOM 元素的重用,因此用另一个包含部分重叠对象的数组来做替换,仍会是一种非常高效的操作。

15. 在点击事件中 访问事件对象 event







function warn(message, event) {
  // 这里可以访问原生事件
  if (event) {
    event.preventDefault()
  }
  alert(message)
}

16. 生命周期

lifecycle.16e4c08e.png

17. 监听器 watch() 和 watchEffect()

watch 函数用来侦听特定的数据源,并在回调函数中执行副作用。
默认情况是惰性的,也就是说仅在侦听的源数据变更时才执行回调。


你不能直接侦听响应式对象的属性值

const obj = reactive({ count: 0 })

// 错误,因为 watch() 得到的参数是一个 number
watch(obj.count, (count) => {
  console.log(`count is: ${count}`)
})

这里需要用一个返回该属性的 getter 函数


// 提供一个 getter 函数
watch(
  () => obj.count,
  (count) => {
    console.log(`count is: ${count}`)
  }
)

简单来说,
侦听reactive数据

const state = reactive({ nickname: "xiaofan", age: 20 });
// 修改age值时会触发 watch的回调
watch(
  () => state.age,
  (curAge, preAge) => {
    console.log("新值:", curAge, "老值:", preAge);
  }
);

侦听ref数据

const year = ref(0);
watch(year, (newVal, oldVal) => {
  console.log("新值:", newVal, "老值:", oldVal);
});

同时侦听多个数据

// 当我们需要侦听多个数据源时, 可以进行合并, 同时侦听多个数据:
watch([() => state.age, year], ([curAge, newVal], [preAge, oldVal]) => {
console.log("新值:", curAge, "老值:", preAge); console.log("新值:", newVal,
"老值:", oldVal); });

侦听复杂的嵌套对象

const state = reactive({
  room: {
    id: 100,
    attrs: {
      size: "140平方米",
      type: "三室两厅",
    },
  },
});
// 注意在此使用的 watch() 方法中的第三个参数: deep:true
watch(
  () => state.room,
  (newType, oldType) => {
    console.log("新值:", newType, "老值:", oldType);
  },
  { deep: true }
);

//  补充: 默认情况下,watch 是惰性的, 那什么情况下不是惰性的, 可以立即执行回调函数呢?
// 其实使用也很简单, 给第三个参数中设置immediate: true 即可。

stop 停止监听
组件中创建的watch监听,会在组件被销毁时自动停止
如果在组件销毁之前我们想要停止掉某个监听, 可以调用watch()函数的返回值

const xxx = watch(.....);


setTimeout(()=>{
    // 停止监听
    xxx()
}, 3000)

和 watchEffect() 对比

// watch已经能满足监听的需求,为什么还要有watchEffect呢
// 还是观察对比, 找区别吧

watchEffect(
     () => {
        console.log(数据1);
        console.log(数据2);
      }
);

上方代码执行结果如下:
执行结果首先打印一次数据1数据2的值;
然后每隔一秒,再打印数据1数据2的值
从上面的代码可以看出, watchEffect 并没有像 watch 一样需要先传入依赖,
watchEffect 会自动收集依赖, 只要指定一个回调函数即可。
在组件初始化时, 会先执行一次来收集依赖,
然后当收集到的依赖中数据发生变化时, 就会再次执行回调函数。
所以总结对比如下:

  • watchEffect 不需要手动传入依赖
  • watchEffect 会先执行一次用来自动收集依赖
  • watchEffect 无法获取到变化前的值, 只能获取变化后的值

18. 事件

在组件的模板表达式中,可以直接使用 $emit 方法触发自定义事件


可以给事件带参数


$emit 方法不能在组件的

defiineEmits() 宏函数使用的注意事项

  • defineEmits()不能在子函数中使用, 它必须直接放置在

  • 如果你正在搭配 TypeScript 使用

19. 事件 配合 v-model 来完成 组件内的 input 输入框数据绑定

事件 配合 v-model 来完成 组件内的 input 输入框数据绑定


















<组件
  :modelValue="searchText"
  @update:modelValue="newValue => searchText = newValue"
/>


20. getter setter 配合 v-model 来完成 组件内的 input 输入框数据绑定






21. 依赖注入(vue3, 所以只能出现在 setup 中)

provide-inject.3e0505e4.png
  • 数据提供者
// 在组件内使用
import { provide } from 'vue'
provide(名称 , 值)

// 给整个app使用
app.provide(名称 , 值)

// 值 也可以是 响应式的值, 
// 使后代组件可以由此和提供者建立响应式的联系
// 一个组件可以多次调用 provide(),
// 使用不同的注入名,注入不同的依赖值。

// 给值套上 readonly, 来不让注入方修改值
app.provide(名称 , readonly(值))

  • 数组注入
// 使用 inject() 函数
const aaa = inject('名称')

// 没有提供者的时候, 可以使用默认值
// 如果没有祖先组件提供 "message"
// `value` 会是 "这是默认值"
const value = inject('名称', '这是默认值')

// 默认值 也可以是一个工厂函数
const value = inject('名称', ()=>{。。。。})


22. 异步组件加载


// 异步组件 的 局部注册
import { defineAsyncComponent } from 'vue'

const 异步组件 = defineAsyncComponent(() => {
  
  // 情况1:
  return new Promise((resolve, reject) => {
    // ...从服务器获取组件
    resolve(/* 获取到的组件 */)
  })
  
  // 情况2: 
  return import('./组件.vue')
  
  
})

// 异步组件 的 全局注册
app.component('异步组件', defineAsyncComponent(() =>
  import('./组件.vue')
))

// 附加属性或功能
const AsyncComp = defineAsyncComponent({
  // 加载函数
  loader: () => import('./Foo.vue'),

  // 加载异步组件时使用的组件
  loadingComponent: LoadingComponent,
  // 展示加载组件前的延迟时间,默认为 200ms
  delay: 200,

  // 加载失败后展示的组件
  errorComponent: ErrorComponent,
  // 如果提供了一个 timeout 时间限制,并超时了
  // 也会显示这里配置的报错组件,默认值是:Infinity
  timeout: 3000
})

// ***也可以搭配  组件


23. 内部组件 ---

让我们可以在组件树上层等待下层的多个嵌套异步依赖项解析完成,
并可以在等待时渲染一个加载状态。

可以等待的异步依赖有两种

  1. 带有异步 setup() 钩子的组件。这也包含了使用

组将用法:


  
  

  
  

组件有两个插槽:#default#fallback

#default#fallback 中都只能接受一个根元素

在可能的时候都将显示默认槽中的节点。否则将显示后备槽中的节点。

组件会触发三个事件:pendingresolvefallback

我们常常会将 等组件结合。
要保证这些组件都能正常工作,嵌套的顺序非常重要。


  

24. ctx 属性

vue3.x 开发环境之下, 可以看到$router$store、声明的变量和方法等
但 在 vue3.x 生产换件之下, 生产环境的ctx$router$store没有了,其他属性也都没有了,不能通过ctx.$routerctx.$store访问router和store,因此ctx可以说对我们没有用,应该避免在代码中使用ctx

所以, : 对于网上一些其他文档使用ctx.$routerctx.$store访问router和store的应该小心避坑,注意开发环境和生产环境的差别

25. 关于生命周期中的一些区别:

执行顺序方面:

3中的setup相当于2中的beforeCreate和created的合并。
vue3.x中会先执行`setup`方法,再执行兼容2.x的其他方法,比如`data`、`computed`、`watch`等,
并且在`setup`执行过程中,无法访问`data`中定义的属性,因为此时还未执行到`data`方法
又并且,setup 函数只走一遍

mount挂载的区别:

  • 2.x会使用挂载元素的outerHTML作为template,并替换挂载元素
  • 3.x会使用挂载元素的innerHTML作为template,并且只替换挂载元素的子元素

26. 关于元素或组件引用的问题

2.x可以在组件挂载之后通过this.$el访问组件根元素
3.x中没有this了, 一切都发生变化了:

3.x去掉了this,但支持了Fragment,所以this.$el没有存在的意义,建议通过refs访问DOM
在组合式 api 中, reactive refstemplate refs 的概念已经是统一的。为了获得对模板内元素或组件实例的引用,我们可以像往常一样在 setup() 中声明一个 ref 并返回它




在 v-for 中 使用 refs



27. 自定义指令 directive 的改动

// vue2.x
export default {
  name: 'YourDirectiveName',
  bind(el, binding, vnode, oldVnode) {},
  inserted(...) {},
  update(...) {},
  componentUpdated(...) {},
  unbind(...) {}
}

// vue3.x
export default {
  beforeMount(el, binding, vnode, oldVnode) {},
  mounted(...) {},
  beforeUpdate(...) {},
  updated(...) {},
  beforeUnmount(...) {},
  unmounted() {...}
}

28. 对 render 方法的 改动

vue、react都提供了render方法渲染html模板,直接使用render方法的还是比较少,毕竟有templateJSX,对于确实需要自定义render方法渲染模板内容的,具体变动如下:

// vue2.x
export default {
  render(h) {
    return h('div')
  }
}

// vue3.x
import { h } from 'vue'
export default {
  render() {
    return h('div')
  }
}

29. 对事件总线 EventBus 的改动

在Vue2.x中可以通过EventBus的方法来实现组件通信

// 声明实例
var EventBus = new Vue()
Vue.prototype.$globalBus = EventBus
// 组件内调用
this.$globalBus.$on('my-event-name', callback)
this.$globalBus.$emit('my-event-name', data)

在vue3.x中移除了 $on$off等方法,而是推荐使用mitt方案来代替:

// 声明实例
import mitt from 'mitt'
const emitter = mitt()


// 组件内调用
// 监听所有事件
emitter.on('*', (type, e) => console.log(type, e))

// 监听个别事件
emitter.on('my-event-name', callback)
emitter.emit('my-event-name', data)


// 清除所有事件
emitter.all.clear()

30. 状态管理---vuex的区别

创建

// Vue2
export default new Vuex.Store({
  state: {
    count:1
  },
  mutations: {
    inc(state){
      state.count ++ 
    }
  },
  actions: {
  },
  modules: {
  }
})

// Vue3
export default Vuex.createStore({
  state: {
    count:1
  },
  mutations: {
    add(state){
      state.count ++ 
    }
  },
  actions: {
  },
  modules: {
  }
});

使用

// Vue2
export default {
  name: "Home",
  data() {
    return {
        state: this.$store.state
    };
  },
  computed: {
    double() {
      return this.$store.state.count * 2;
    },
  },
  methods: {
    add() {
      this.$store.commit("add");
    }
  }
};
// Vue3
import { computed,  reactive } from "vue";
import { useStore } from "vuex";
export default {
  setup() {
    const store = useStore()
    const state = store.state
    const double = computed(() => store.state.count * 2)

    const add = () => {
      store.commit("add");
    };
    return { state, add ,double};
  }
};

31. 在vue3中自定义 hook 函数

类似的, 钩子函数以 use 开头
创建 usePerosn.js 文件

import { computed, reactive } from "vue"

export default function (per = {name: "zhangsan", age:12}) {
    
    const ren = reactive(per)
    const changeName = (name) => {
        ren.name = name
    }
    const zengjiaAge = (num) => {
        ren.age = ren.age + num
    }
    const getBirth = computed(()=> {
        return 2022 - ren.age
    })
    return {
        ren , 
        changeName,
        zengjiaAge,
        getBirth
    }
}

在组件中 使用 该 person 钩子




32. teleport , 将其所包含的组件的层级结构显示到特定的 地方

// 比如说组件A这样定义


// 加入home界面要使用组件A这个弹框
// home界面


// 这样就是: 虽然home界面通过数据控制了弹框组件A,
// 但其实该弹框组件A是展示在 其他DIV中的, 而不是 home 界面里面

你可能感兴趣的:(vue3 知识点 和 对比 部分总结, 更新中。。。。。)