vue3尝试整理

vue3

安装创建项目

  • 安装/升级脚手架
    • 未安装过vue-cli npm install -g @vue/cliyarn global add @vue/cli
    • 已安装直接升级 npm update -g @vue/cliyarn global upgrade --latest @vue/cli
  • 创建项目
    1. 使用官方的vue-cli脚手架升级安装
      • vue create vue3-test 创建名为vue3-test的项目 cd vue3-test 切入目录运行 yarn servenpm run serve
      • 忘记选择3.0可以yarn add vue-next自动升级到vue3的版本
      • 设置别名 tsconfig.json
          {
              "compilerOptions": {
                  "paths": {
                      "@/*": ["src/*"]
                  }
              }
          }
      
    2. 使用vite安装 (Vite当前仅适用于Vue3.x!!!)
      • git clone https://github.com/vitejs/vite.git 拉取代码到本地 cd vite进入目录 yarn 安装依赖 yarn build 打包 yarn link 创建项目 yarn create vite-app vue3-test 进入目录 cd project-name 安装依赖 yarnnpm i 运行 yarn devnpm run dev
      • 设置别名,新建vite.config.js文件
          module.exports={
              alias: {
                  '/@/': path.resolve(__dirname, './src'),
                  '/@view/': path.resolve(__dirname, './src/views'),
                  '/@components/': path.resolve(__dirname, './src/components'),
                  '/@utils/': path.resolve(__dirname, './src/utils'),
              },
          }
      

与vue2的区别

  • 全部采用函数式写法替换原来类的写法,源码用ts编写中实现了对ts更好的支持
  • 移除了原有的生命周期函数和data(vue3中去掉了data,使用setup的返回值来给模板绑定value,return的对象如果是常量不会变成响应式数据)、computed、watch、method等vue2中的对象,去掉了this(this在setup中不可用。由于setup()在解析 2.x 选项前被调用,setup() 中的 this 将与 2.x 选项中的 this 完全不同。同时在 setup() 和 2.x 选项中使用 this 时将造成混乱。),并且去除了过滤器api -> filter
  • 完全兼容vue2,两种写法可同时存在
  • 组件中同时存在两种写法时,当setup返回值中定义的方法和methods中的方法同名时,会抛出错误.定义的数据和data定义的数据字段相同时,会被data定义的字段覆盖
  • vue3采用proxy的方式实现数据代理,只会代理第一层数据,在 vue2 中响应式数据需要通过 data() 初始化预先定义好,但是在 vue3 之后,data 中的数据不再进行响应式追踪,而是将它转化为proxy 代理再进行追踪更新,避免了vue2中对data的深层递归,提升了组件渲染性能在vue3中,它将任何在data()函数中定义的数据,会使用getter或setter遍历所有的property,将其转化成proxy代理。

Vue3流程图

本部分参考https://juejin.cn/post/6898750262614163470

  • app 挂载流程
    1. 调用 createApp(App) 生成应用实例,然后调用mount方法
    2. 调用 mount 的阶段,首先根据入口组件 App 创建VNode,接着触发 render(reactive+effect) 函数,整个render的阶段,就是 Patch 所有 Vnode 的阶段,当中分为初始化挂载与更新组件
      mount
    3. 对于内部 patch 的阶段,会对不同节点类型有不同的处理,主要是组件与element,组件就继续执行创建、挂载等等
      data/props 挂到 ctx 上

组合式APIcomposition API使用(核心)

  • createApp创建应用(较之前new Vue的方式在使用上基本无差别)
        //main.js中先导入createApp模块
        import { createApp } from 'vue'
        import App from './App.vue';
        import './index.css';//全局样式
        // 使用createApp方法将APP.vue入口文件放进去,最后挂载
        createApp(App).mount('#app')
    
  • setup函数:在组件内使用 Composition API 的入口点
    • 创建组件实例-初始化props-调用setup(setup只执行一次,位于组件生命周期beforeCreate钩子之前,所以拿不到当前实例this,不能用this来调用vue2写法中定义的方法)
    • 在vue2中,data数据需要在使用的时候调用this访问,现在vue3中使用reactive,ref来设置响应式数据。
    • 使用:
      • 模板中:setup 返回一个对象,则对象的属性将会被合并到组件模板的渲染上下文中()
      • 渲染函数 / JSX 中:可返回一个函数,函数中也能使用当前 setup 函数作用域中的响应式数据
          <template>
          <div>{{ count }} {{ object.foo }}</div>
          </template>
          <script>
          import { ref, reactive } from 'vue'
          //import { h,ref, reactive } from 'vue'
          export default {
              setup() {
              const count = ref(0)
              const object = reactive({ foo: 'bar' })
              return { count,object }// 1.暴露给模板
              // return () => h('div', [count.value, object.foo]) //2.返回函数
              },
          }
          </script>
      
      • setup可选择性暴露方法
      setup () {
          function Func() {
              console.log(1111);
          }
          return {
              Func, // return之后,该函数绑定到了组件实例上,可以在模版中直接使用
          };
      }
      
      • setup的第一个参数,就是props,setup的第二个参数,提供了上下文对象,并且从2.x中选择性暴露了一些属性。例如:需要用到this.$emit用context.emit方法来替代
          import { SetupContext } from 'vue';
          emits: ['eventName'],
          // props - 组件接受到的属性, ctx- 上下文 
          setup(props, ctx: SetupContext){
              ctx.emit('event-name', value);
          }
      
  • 生命周期函数
    • 生命周期都变成了回调的形式,只在setup函数中使用有效,可以一次写多个相同的生命周期函数,按照注册顺序执行,生命周期钩子注册函数只能在 setup() 期间同步使用,组件实例上下文也是在生命周期钩子同步执行期间设置的,因此,在卸载组件时,在生命周期钩子内部同步创建的侦听器和计算状态也将自动删除。
    import { onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted ,onErrorCaptured, onRenderTriggered } from 'vue';
     setup() {
        //beforeCreate 和 created -> 使用 setup()
        onBeforeMount(() => {
            //beforeMount -> onBeforeMount
        });
        onMounted(() => {
            //mounted -> onMounted
        });
        onBeforeUpdate(() => {
            //beforeUpdate -> onBeforeUpdate
        });
        onUpdated(() => {
            //updated -> onUpdated
        });
        onBeforeUnmount(() => {
            //beforeDestroy -> onBeforeUnmount
        });
        onUnmounted(() => {
            //destroyed -> onUnmounted
        });
        onErrorCaptured((e) => {
            //errorCaptured -> onErrorCaptured
        });
        onRenderTriggered((e) => {
            // 检查哪个依赖性导致组件重新渲染
        });
    },
    
    
  • getCurrentInstance 支持在setup函数中访问自己的实例
        import { getCurrentInstance } from 'vue';
        setup() {
            const internalInstance = getCurrentInstance();
        }
    
  • ref:简单的响应式数据
    • 接受一个参数值并返回一个响应式且可改变的ref 对象,ref 对象拥有一个指向内部值的单一属性.value,如果传入 ref 的是一个对象,将调用 reactive 方法进行深层响应转换
    • setup 返回的 ref 在模板中会自动解开,可直接渲染在页面上,不需要.value
    • 当 ref 作为 reactive 对象的 property 被访问或修改时,也将自动解套 value 值
    • 当嵌套在 reactive Object 中时,ref 才会解套。从 Array 或者 Map 等原生集合类中访问 ref 时,不会自动解套
        <template>
            <div>{{ count }}</div>
        </template>
        <script>
        export default {
            setup() {
                const count = ref(0);
                const state = reactive({count});
                console.log(count.value); // 0
                count.value++;
                console.log(count.value); // 1
                state.count = 2;
                console.log(count.value); // 2
                const otherCount = ref(3);
                state.count = otherCount; //替换旧的ref
                console.log(state.count); // 3
                //需要.value的情况
                const arr = reactive([ref(0)]);
                console.log(arr[0].value);
                const map = reactive(new Map([['foo', ref(0)]]));
                console.log(map.get('foo').value);
                return { count }
            }
        }
        </script>
    
  • reactive:接收一个普通**对象*然后返回该对象的响应式副本,等同2.x的Vue.observable()
    • 响应式转换是深层的,会影响对象内部所有嵌套的属性。
  • computed:计算属性
    • 传入一个 getter 函数,返回一个默认不可手动修改的 ref 对象
          const count = ref(1);
          const plusOne = computed(() => count.value + 1);
          console.log(plusOne.value); // 2
          plusOne.value++;// 报错!不可修改
      
    • 传入一个拥有 get 和 set 函数的对象,创建一个可手动修改的计算状态
          const count = ref(1);
          const plusOne = computed({
              get: () => count.value + 1,
              set: (val) => { count.value = val - 1},
          });
          plusOne.value = 1;
          console.log(count.value); // 0
      
  • readonly:获取一个对象 (响应式或纯对象) 或 ref 并返回原始代理的只读代理。只读代理是深层的:访问的任何嵌套 property 也是只读的.
        const original = reactive({ count: 0 })
        const copy = readonly(original)
        watchEffect(() => { // 依赖追踪
            console.log(copy.count)
        })
        // original 上的修改会触发 copy 上的侦听,可修改
        original.count++ 
        // 只读,无法修改 copy 并会被警告
        copy.count++ // Set operation on key "count" failed: target is readonly.
    
  • watchEffect:立即执行传入的一个函数,并响应式追踪其依赖,并在其依赖变更时重新运行该函数。
    • 侦听数据变化:
          const count = ref(0)
          watchEffect(() => console.log(count.value))//0
          setTimeout(() => { count.value++; }, 100) //1
      
    • 停止侦听:
      • 当 watchEffect 在组件的 setup() 函数或生命周期钩子被调用时, 侦听器会被链接到该组件的生命周期,并在组件卸载时自动停止。
      • 显式调用返回值以停止侦听
            const stop = watchEffect(() => {
                /* ... */
            })
            // 之后
            stop()
        
    • 清除副作用:侦听副作用传入的函数可以接收一个 onInvalidate 函数作入参, 用来注册清理失效时的回调。当以下情况发生时,这个失效回调会被触发:
      • 副作用即将重新执行时
      • 侦听器被停止 (如果在 setup() 或 生命周期钩子函数中使用了 watchEffect, 则在卸载组件时)
        watchEffect((onInvalidate) => {
            const token = performAsyncOperation(id.value)
            onInvalidate(() => {
                // id 改变时 或 停止侦听时
                // 取消之前的异步操作
                token.cancel()
            })
        })
      
  • watch:等同vue2.x中watch
    • ,watch 需要侦听特定的数据源,并在回调函数中执行副作用。默认情况是懒执行的,也就是说仅在侦听的源变更时才执行回调。
    • 对比watchEffect
      • 不同:
        • watch允许懒执行副作用;
        • watch更明确哪些状态的改变会触发侦听器重新运行副作用;
        • watch访问侦听状态变化前后的值。
      • 相同:
        • 在停止侦听, 清除副作用 (相应地 onInvalidate 会作为回调的第三个参数传入),副作用刷新时机 和 侦听器调试 等方面行为一致.
    • 侦听使用
      • 单个数据源
            const state = reactive({ count: 0 });// 侦听一个 getter
            watch(() => state.count,(count, prevCount) => {/* ... */});
            const count = ref(0);// 直接侦听一个 ref
            watch(count, (count, prevCount) => {/* ... */});
        
      • 多个数据源
            watch([fooRef, barRef], ([foo, bar], [prevFoo, prevBar]) => {/* ... */});
        
  • 模板 Refs
    • 当使用组合式 API 时,reactive refs 和 template refs 的概念已经是统一的。
          <template>
              <div ref="root"></div>
          </template>
          <script>
          import { ref, onMounted } from 'vue'
          export default {
              setup() {
                  const root = ref(null)
                  onMounted(() => {
                      // 在渲染完成后, 这个 div DOM 会被赋值给 root ref 对象
                      console.log(root.value) // <div/> 等同vue2.x中console.log(this.$refs.root.value);
                  })
                  return { root }
              },
          }
          </script>
      
    • 配合 render 函数 / JSX 的用法
          export default {
              setup() {
                  const root = ref(null)
                  return () => h('div', { ref: root});  // 使用 JSX
                  return () => <div ref={root} />
              },
          }
      
    • 在 v-for 中使用
      • v-for中需要使用函数型的ref来自定义处理方式:
          <template>
          <div v-for="(item, i) in list" :ref="el => { divs[i] = el }">
              {{ item }}
          </div>
          </template>
          <script>
          import { ref, reactive, onBeforeUpdate } from 'vue'
          export default {
              setup() {
              const list = reactive([1, 2, 3])
              const divs = ref([])
              // 确保在每次变更之前重置引用
              onBeforeUpdate(() => {
                  divs.value = []
              })
              return { list, divs }
              },
          }
          </script>
      
  • 响应式工具集
    • unref:如果参数是一个 ref 则返回它的 value,否则返回参数本身。它是 val = isRef(val) ? val.value : val 的语法糖。
          function useFoo(x: number | Ref<number>) {
              const unwrapped = unref(x) // unwrapped 一定是 number 类型
          }
      
    • toRef: 可以用来为一个 reactive 对象的属性创建一个 ref,这个 ref 可以被传递并且能够保持响应性。
          const state = reactive({
              foo: 1,
              bar: 2,
          })
          const fooRef = toRef(state, 'foo')
          fooRef.value++
          console.log(state.foo) // 2
          state.foo++
          console.log(fooRef.value) // 3
      
      • 将一个 prop 中的属性作为 ref 传给组合逻辑函数时
          export default {
              setup(props) {
                  useSomeFeature(toRef(props, 'foo'))
              },
          }
      
    • toRefs: 把一个响应式对象转换成普通对象,该普通对象的每个 property 都是一个 ref,和响应式对象 property 一一对应。
      • 当想要从一个组合逻辑函数中返回响应式对象时,toRefs可以解构 / 扩展(使用 … 操作符)返回的对象,并不会丢失响应性:
           function useFeatureX() {
                const state = reactive({
                    foo: 1,
                    bar: 2,
                })
                // 对 state 的逻辑操作
                // 返回时将属性都转为 ref
                return toRefs(state)
            }
            export default {
                setup() {
                    // 可以解构,不会丢失响应性
                    const { foo, bar } = useFeatureX()
                    return { foo,bar }
                },
            }
        
        
    • isRef: 检查一个值是否为一个 ref 对象。
    • isProxy: 检查一个对象是否是由 reactive 或者 readonly 方法创建的代理。- isReactive: 检查一个对象是否是由 reactive 创建的响应式代理。如果这个代理是由 readonly 创建的,但是又被 reactive 创建的另一个代理包裹了一层,那么同样也会返回 true。
    • isReadonly: 检查一个对象是否是由 readonly 创建的只读代理。
  • 高级响应式系统 API
    • customRef:用于自定义一个 ref,可以显式地控制依赖追踪和触发响应,接受一个工厂函数,两个参数分别是用于追踪的 track 与用于触发响应的 trigger,并返回一个带有 get 和 set 属性的对象。
      • 使用自定义 ref 实现带防抖功能的 v-model
            function useDebouncedRef(value, delay = 200) {
                let timeout
                return customRef((track, trigger) => {
                    return {
                        get() {
                            track()
                            return value
                        },
                        set(newValue) {
                            clearTimeout(timeout)
                            timeout = setTimeout(() => {
                            value = newValue
                            trigger()
                            }, delay)
                        },
                    }
                })
            }
            export default {
                setup() {
                    return { text: useDebouncedRef('hello') }
                }
            }
        
        
    • markRaw:显式标记一个对象为“永远不会转为响应式代理”,函数返回这个对象本身。
      • 如果被 markRaw 标记了,即使在响应式对象中作属性,也依然不是响应式的
        const foo = markRaw({})
        console.log(isReactive(reactive(foo))) // false
        const bar = reactive({ foo })
        console.log(isReactive(bar.foo)) // false
      
    • shallowReactive:只为某个对象的私有(第一层)属性创建浅层的响应式代理,不做深层次、递归地响应式代理,而只是保留原样。
          const state = shallowReactive({
              foo: 1,
              nested: { bar: 2 },
          })
          // 变更 state 的自有属性是响应式的
          state.foo++
          // ...但不会深层代理
          isReactive(state.nested) // false
          state.nested.bar++ // 非响应式
      
    • shallowReadonly:只为某个对象的自有(第一层)属性创建浅层的只读响应式代理,同样也不会做深层次、递归地代理,深层次的属性并不是只读的。
          const state = shallowReadonly({
              foo: 1,
              nested: { bar: 2 },
          })
          // 变更 state 的自有属性会失败
          state.foo++
          // ...但是嵌套的对象是可以变更的
          isReadonly(state.nested) // false
          state.nested.bar++ // 嵌套属性依然可修改
      
    • shallowRef:创建一个 ref,将会追踪它的 .value 更改操作,但是并不会对变更后的.value 做响应式代理转换(即变更不会调用 reactive)
           const foo = shallowRef({})
           // 更改对操作会触发响应
           foo.value = {}
           // 但上面新赋的这个对象并不会变为响应式对象
           isReactive(foo.value) // false
      
    • toRaw:返回由 reactive 或 readonly 方法转换成响应式代理的普通对象。这是一个还原方法,可用于临时读取,访问不会被代理/跟踪,写入时也不会触发更改。不建议一直持有原始对象的引用。谨慎使用
          const foo = {}
          const reactiveFoo = reactive(foo)
          console.log(toRaw(reactiveFoo) === foo) // true
      
  • nextTick 写法略有不同,原理相同
    import { nextTick } from 'vue';
    export default defineComponent({
        setup () {
            nextTick(() => {
            })
        }
    })
    
  • vue-router 组件中使用路由时用useRoute和useRouter
```js
    import {useRoute, useRouter} from 'vue-router'
    const route = useRoute(); // 相当于 vue2 中的this.$route,获取当前路由
    const router = useRouter(); // 相当于 vue2 中的this.$router,用于路由跳转
```
  • vuex
        import {useStore} from 'vuex'
        setup(){
            const store = useStore(); // 相当于 vue2中的 this.$store
            store.dispatch(); // 通过store对象来dispatch 派发异步任务
            store.commit(); // commit 修改store数据
            let category = computed(() => store.state.home.currentCagegory)
            return { category }
        }
    
  • jsx定义组件
        export const AppMenus = defineComponent({
            setup() {
                return () => {
                    return (
                        <div class="app-component">
                            <h1>组件</h1>
                        </div>
                    );
                };
            },
        });
    
  • 插槽(一个不带 name 的 出口会带有隐含的名字name=“default”)
       <div class="container">
        <header>
            <slot name="header"></slot>
        </header>
        <main>
            <slot></slot>
        </main>
        <footer>
            <slot name="footer"></slot>
        </footer>
        </div>
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值