Composition API

420 阅读4分钟
Vite 简单介绍
  • Vite在开发环境下不需要打包,开发模式下vite使用浏览器原生支持的ES Module的方式加载模块
  • Vue-Cli 在开发模式下必须对项目打包才可以运行 vite开启的web服务器会开启劫持vue的请求,会把.vue文件解析成js文件,并把响应头的content-type设置成为 application/javascript,告诉浏览器现在发送的是一个js脚本。 服务器会对浏览器不能识别的文件进行特殊处理,如果是单文件组件, 会调用complierSFC编译单文件组件并把编译的结果返回给浏览器
Composition API

了解: createApp : 创建vue对象 setup: compositionApi的入口 reactive: 创建响应式对象的

setup是在props被解析完毕,但是组件被创建之前执行的, 所以setup内部无法通过this获取组件实例, 及data, computed, methods 等。

    setup(props, context){
      // 第一个参数 props, 是个响应式对象,不能被解构
      // 第二个参数 context对,象 包含attrs、emit、slots
    }
setup中也可以使用生命周期的钩子函数
  • onRenderTrigger 首次调用render不会触发
  • onRenderTracked 首次调用render触发
reactive/ref/toRefs

都是用来创建响应式数据的。 具体结合🌰来看:

reactive的使用
    function useMousePosition() {
      const position = reactive({ //把一个对象转换成响应式对象,并且该对象的嵌套属性也会转换成响应式对象,返回的是一个proxy对象
        x: 0,
        y: 0
      })
      const updated = (e) => {
        position.x = e.pageX;
        position.y = e.pageY;
      }
      onMounted(() => {
        window.addEventListener('mousemove', updated)
      });
      onUnmounted(() => {
        window.remmoveEventListener('mousemove', updated)
      });
      return toRefs(position)
    }

🌰
如果我们想在模版里直接使用x,y来获取鼠标位置,修改代码如下

// 尝试将position解构
  setup(props) {
    const {x, y} = useMousePosition();
    return {
      x,
      y
    }
  },

发现x, y 不再是响应式了。

🤔: 在解构前position是个proxy对象,在访问position中的属性时,会调用代理对象中的getter,拦截收集依赖。当属性变化时会调用代理对象中的setter进行拦截触发更新。
那么我们看看解构后发生了什么。
先来看下babel对解构的降级处理:

image.png

可以看到在降级之后,其实就是用两个变量来接收了position.x, position.y。 而我们知道,基本类型的赋值是把值在内存中复制一份。而这里的x,y也仅仅只是一个基本类型的变量,与代理对象无关。所以当重新给x,y赋值时,也无法调用代理对象的setter, 无法触发更新的操作。所以是不能对当前的响应式对象进行结构的。

这时,需要用到一个新的Api:toRefs。

toRefs的使用

toRefs 可以把一个响应式对象中的所有属性也转换成响应式。 所以可以利用toRefs来进行结构的操作

function useMousePosition() {
    ...
    return toRefs(position)
}
ref的使用

主要可以把一个基本类型的数据转换成响应式对象。
(ref如果接收一个对象,内部就调用了reactive 返回一个代理对象)
如果接收一个基本数据类型,内部会创建一个只有value属性的对象(模版中使用该对象时可以省略value),该对象的value属性具有getter,setter。 在getter中收集依赖,setter中触发更新。
结合🌰来看

  <div id="app">
    <button @click='increment'>点击</button>
    <span>{{count}}</span>
  </div>

 function useIncrement(){
    const count = ref(0);
     return {
       count, //此时的count是一个对象
       increment:() => {
         count.value ++ 
      }
    }
 }

 const app = createApp({
   setup(props) {
     return {
      ...useIncrement()
     }
   }
 })
computed用法

🌰

    ...
   <span>{{activeCount}}</span>
   <button @click="push">增加</button>
    ...
    
    const data = [
      { text: "read", completed: false, },
      { text: "excise", completed: true, },
      { text: "sleep", completed: false, },
    ]
    setup(props) {
       const todos = reactive(data)
       const activeCount = computed(() => {
          return todos.filter(it => !it.completed).length
        });
       return {
         activeCount,
          push() {
            todos.push({
              text: "eee",
              completed: false
            })
         }
      }
    }
watch 用法

Watch 的三个参数

  • 第一个参数:要监听的数据
  • 第二个参数:监听到数据变化后执行的函数,这个函数有两个参数分别是新值和旧值
  • 第三个参数:选项对象,deep 和 immediate Watch 的返回值
  • 取消监听的函数 🌰:
  <div id="app">
    请输入你的问题:
    <input type="text" v-model='question'>
    <p>
      {{answer}}
    </p>
  </div>
  ...
   const apiUrl = 'https://www.yesno.wtf/api'
    const app = createApp({
      setup(props) {
        const question = ref('');
        const answer = ref('');
        watch(question, async(newVl, oldVl) => {
          const response = await fetch(apiUrl); 
          const data = await response.json();
          answer.value = data.answer
        });
        return {
          question,
          answer
        }
      }
    })
watchEffect

接收一个函数作为参数,监听函数内响应式数据的变化。初始就会执行一次,当监听的数据变化时会再次触发。 并返回一个函数,取消对数据的监听

 <div id="app">
    {{count}}
    <button @click='increment'>count增加</button>
    <p>
     取消监听: <button  @click='stop'>stop</button> 
      <!-- 点击后控制台不再打印 -->
    </p>
  </div>
  ...
  const app = createApp({
      setup(props) {
        const count = ref(0);
        const stop = watchEffect(() => {
          console.log(count.value);
        })
        return {
          count,
          stop,
          increment(){
            count.value ++ 
          }
        }
      }
    })