Vue3学习笔记1 vite、Volar、setup、script-setup、ref、 shallowRef、triggerRef、customRef

寒假学的时候属于是有点过于敷衍了,现在update一下😮‍💨
最近是跟ts一起学的,所以里面会有ts相关的内容(无伤大雅无伤大雅


1. 创建Vue3.0工程(vue-cli / vite)

  1. 使用vue-cli创建
// 确保@vue/cli版本在4.5.0以上
vue --version
// 安装或者升级你的@vue/cli
npm i -g @vue/cli
// 创建(xxx为项目名)
vue create xxx
// 启动
cd xxx
npm run serve
  1. 使用vite创建
  • 什么是vite
    新一代前端构建工具
  • 优势:
    开发环境中无需打包操作,可以快速的冷启动
    轻量快速的热重载
    真正的按需编译,不再等待整个应用编译完成
// 创建
npm init vite@latest
// 输入项目名
xxx
// 之后可以选择要使用的框架
// vanilla	vanilla-ts
// vue	vue-ts
// react	react-ts
// preact	preact-ts
// lit	lit-ts
// svelte

// 安装依赖
npm i
// 启动
npm run dev

2. vite构建项目目录

和其他构建工具生成的目录相同的地方就不提了。
最大的一个不同在于vite项目中的index.html文件在根目录中,而不是在public文件夹中,在vite项目中index.html作为项目的入口文件,而不同于其他使用js文件作为入口文件

vite.config.js是vite的配置文件


3. Volar

在学习vue2时,我们在vscode中安装的插件是Vetur,但是Vetur对vue3不太友好,vue3的一些语法在Vetur中会报错。
所以学习vue3时我们会使用Volar
⚠️:VolarVetur会冲突,使用时要禁用Vetur

第二个提供ts支持:

在这里插入图片描述
在这里插入图片描述


4. composition api

  • vue2中我们使用option api,在data中定义数据,在methods中定义方法,在props中设置接受参数…这顺其自然的造成了代码分割,一个功能被分割在多个地方,方法嵌套方法,方法之间共享变量,功能之间互相使用this导致了强耦合。(代码量一多就是堆成了屎山
  • 所以vue3出现了composition api(组合式api),将代码通过功能分块写,一个功能写在一个地方,(代码量多的时候可以配合自定义hooks抽出功能模块,composition api解耦option api实现低耦合高内聚。
    在这里插入图片描述

5. setup

  • vue3中组合式api的一个配置项函数,作为组合式api的入口
  • 组件中所用到的:数据方法均要配置在setup
  • setup返回值:若返回一个对象,则对象中的属性、方法,均可模板中使用
export default {
  setup() {
    const name = 'AIpoem';	 // data
    
    const sayName = (() => {	// method
    	console.log(name);
    })
    return {
      name,
      sayName
    }
  }
}
  • ⚠️:setup中不要使用this,在这里面找不到组件实例,setup的调用发生在组件创建之前
  • ⚠️:不要跟vue2.x配置混用
    • vue2.x(data、method…)中可以访问到setup中的属性、方法,但setup中无法访问到vue2.x(data、method…)
    • setup不能是一个async函数,否则其返回值不再是return的对象,而是promise

5.1 setup的两个参数

setup函数接受两个参数:

  • props
  • context

5.1.1 props

props是一个响应式对象,用于接收传参
但要注意不能使用解构拿到参数,会消除prop的响应性
解构时使用toReftoRefs

export default {
  props: {
    title: String
  },
  setup(props) {
    console.log(props.title);
    // 错误写法❌
    const { title } = props;
    // 以下两种写法✅
    const { title } = toRef(props, 'title');
    const title = toRefs(props)
  }
}

5.1.2 context

context是一个普通的对象,暴露一些可能有用的值

export default {
  setup(props, context) {
    // 值为对象,包含了:组件外部传递过来,但没有在props配置中声明的属性
    // 相当于vue2中的this.$attrs
    console.log(context.attrs)

    // slots: 收到的插槽内容
    // 相当于vue2中的this.$slots
    console.log(context.slots)

    // 分发自定义事件的函数
    // 相当于vue2中的this.$emit
    console.log(context.emit)

	// 可以用expose暴露一些属性和方法,这些变量和方法可以被外部访问到
    console.log(context.expose)
  }
}

因为context不是响应式的,所以可以直接对其进行解构

export default {
  setup(props, { attrs, slots, emit, expose }) {
    ...
  }
}
5.1.2.1 expose

setup return的所有属性和方法,父组件都能通过ref访问到
但如果我们希望一些属性方法能不被父组件访问,可以定义在expose中,表明只允许外部使用这些属性和方法,未写在expose的属性和方法相当于变成当前组件的私有,外部访问不到

expose的使用:
给它传一个对象, 里面放希望外部能访问的属性和方法

import { ref } from 'vue'
export default {
  setup(props, { expose }) {
    const count = ref(0)
    const add = () => ++count.value
	
	// 这个add方法现在将可以通过父组件的模板 ref 访问
    expose({
      add
    })
	
    return {
      count,
      add
    }
  }
}

5.2 setup语法糖

vue3.2 新增setup语法糖<script setup>
(后面的我应该都会用语法糖了,比较爱😮‍💨

5.2.1 不用再return啦

无需return{},声明的变量、函数、import的内容都可以直接在template中使用
(export default{} 也不需要写)

<script setup>
  import { getX } from './utils'; //import引入的内容
  
  const name = 'AIpoem';	// 变量
  const sayName = (() => {	// 函数
    console.log(name);
  })
</script>

<template>
  <div @click="sayName">{{ name }}</div>
  <div>{{ getX() }}</div>
</template>

⚠️:<script setup>中的代码都会被编译成setup()函数里的内容,这意味着<script setup><script>不同,<script setup>里的代码会在每次组件实例被创建的时候执行,<script>只在组件被首次引入时执行一次

 <script>
  console.log('script'); 	// 多次实例化组件,但只触发一次
  export default {
    setup() {
      console.log('setup');	// 每次实例化组件都触发和<script setup>一样
    }
  }
</script>

5.2.2 引入的组件无需注册

<script setup>import的组件不用在components: {}中注册,可以直接在template中使用

5.2.3 defineProps、defineEmits、defineExpose API

setup在这不是函数了,那怎么拿到propsemits?怎么使用expose
必须使用definePropsdefineEmitsdefineExpose API
vue3.2中并不需要引入 这些可以直接用

  • defineProps替代props,接收父组件传递的数据:
    (父组件正常传就ok)
    <script setup lang="ts">
    // 仅限于ts的功能,类型声明
    defineProps<{ title: string }>();
    
    // 使用defineProps没有给props提供默认值的方式,我们如果需要可以使用withDefaults
    interface Props {
      title: string,
      age: number[]
    }
    withDefaults(defineProps<Props>(), {
      title: 'AIpoem',
      age: () => [10, 20]
    })
    
    // 以下是不使用ts的写法
    defineProps({
      title: String
    });
    
    // 简易写法
    defineProps(['title', ...]);
    
    // 如果需要在script中使用这个prop,可以接收
    const { title } = defineProps({
      title: String
    });
    </script>
    
    模版中不需要props.xx,可以直接使用
    <template>
      <div>父组件传递的值:{{title}}</div>
    </template>
    
  • defineEmits替代emits,用于子组件向父组件传递数据:
    <script setup lang="ts">
    import { ref } from 'vue'
    // 要传递的数据
    const name = ref<string>('AIpoem')
    // 触发的事件名称,传递的数据,返回值
    const emits = defineEmits<{ (e: "send-name", name: string): void }>();
    
    // 以下是不使用ts的写法
    const emits = defineEmits(['send-name']);
    const toEmits = () => {
      // 触发父组件中暴露的send-name方法并携带数据
      emits('send-name', name)
    }
    </script>
    
    • defineExpose替代expose,并且<script setup>默认是封闭的,这就和setup()默认暴露组件的全部内容是不同的,希望外部访问的属性和方法必须显式expose出去
    // 子组件
    <script setup>
    import { ref, defineExpose } from 'vue'const name = ref('AIpoem')
    const b = ref(2)
    //主动暴露组件属性
    defineExpose({
      name
    })
    </script>
    
    <!-- 父组件 -->
    <template>
      <Child ref='childRef' />
      <button @click='getChildData'>通过ref获取子组件的属性</button>
    </template><script setup>
    import { ref } from 'vue'
    import Child from './child.vue'
    const childRef = ref()  // 注册响应数据  
    const getChildData =()=>{
      // 子组件接收暴露出来得值
      console.log(childRef.value.name) //1
    }    
    </script>
    

6. 父组件调用子组件的方法

(补充一下
对应vue2中的this.$refs.xxx

<!-- 子组件 -->
<script setup lang="ts">
import { reactive } from "vue";
const list = reactive<number[]>([1, 2, 3]);

// 把list暴露给父组件
defineExpose({
	list
});
</script>
<script setup lang="ts">
import { onMounted, ref } from "vue";
import Child from "./components/Child.vue";

const c1 = ref();
// setup中dom还未挂载,获取会是undefined❌
console.log(c1.value);

// 可以获取到
onMounted(() => {
  // Proxy {list: Proxy, __v_skip: true}
  console.log(c1.value);
})
</script>

<template>
	<child ref="c1"></child>
</template>

7. ref

  • 接收一个值,返回一个响应式ref对象
  • ref对象只有一个value属性,指向内部值
  • js中操作数据:xxx.value,在template中读取数据:<div>{{ xxx }}</div>
<script setup lang="ts">
import { ref } from "vue";

let message = ref<string>("poem");
const changeMsg = () => {
  message.value = "change poem";
};
</script>

<template>
  <div>{{ message }}</div>
</template>
  • ⚠️:
    接收的数据可以是基本类型,也可以是对象类型
    • 基本类型的数据:响应式是依然通过Object.defineProperty()getset完成的
    • 对象类型的数据:内部求助了vue3.0的一个新函数——reactive函数

8. shallowRef

shallowRef只保证自身响应式,不保证对象里属性的响应式
也就是说这个ref是浅层的

<script setup lang="ts">
import { shallowRef } from "vue";

let message = shallowRef<{ name?: string }>({
	name: "poem",
});

const changeMsg = () => {
  // 里面的属性不具有响应式,以下这样是不具有响应式的
  // message2.value.name = "change poem";

  // 正确写法:可以这样整个对象重新赋值
  message.value = { name: "change poem" };
  console.log(message);
};
</script>

9. triggerRef

手动执行与 shallowRef 关联的任何作用
可以让shallowRef做到属性也能变成响应式的

<script setup lang="ts">
import { shallowRef, triggerRef } from "vue";

let message = shallowRef<{ name?: string }>({
  name: "poem",
});

const changeMsg = () => {
  message.value.name = "change poem";
  triggerRef(message2);
};
</script>

10. customRef

创建一个自定义的ref
必须传一个工厂函数,有tracktrigger两个参数

<script setup lang="ts">
import { customRef } from "vue";

// <T>指定泛型
function useMyRef<T>(value: T) {
  return customRef((track, trigger) => {
    return {
      get() {
        track();
        return value
      },
      set(newValue: T) {
        console.log('设置了新value:', newValue);
        value = newValue;
        trigger();
      }
    }
  })
}
let message = useMyRef<string>('poem')

const changeMsg = () => {
  message.value = 'new poem'
  // 打印:设置了新value: new poem
};
</script>

参考博文:
script-setup
https://juejin.cn/post/7083401842733875208

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值