You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

394 lines
13 KiB

  1. # Vue3
  2. ## 优点
  3. 1. Vue3 支持 Vue2 额大多数特性。
  4. 2. 更好的支持 TypeScript。
  5. 3. 打包大小减少 41%。
  6. 4. 初次渲染快 55%,更新渲染快 133%。
  7. 5. 内存减少 54%。
  8. 6. 使用 proxy 代替 defineProperty 实现数据响应式。
  9. 7. 重写虚拟 DOM 的实现和 Tree-Shaking。
  10. ## API
  11. ### setup
  12. 1. setup 是一个函数。只在初始化时执行一次。以后大部分代码都是在 setup 中写。
  13. 2. 返回一个对象,对象中的属性或方法,模板中可以直接使用。
  14. 3. setup 返回的数据会和 data 和 methods 进行合并,setup 优先级更高。
  15. 4. setup 函数中没有 this。 以后开发都不使用 this
  16. 5. setup 不要写 async 函数。
  17. > 因为 async 函数必须返回一个 json 对象供模板使用,如果 setup 是一个 async 函数,返回的将是一个 promise 对象。
  18. > 如果 setup 是一个 async 函数,那该组件就成了一个异步函数,需要配合 Suspense 组件才能使用
  19. ### ref
  20. 让数据变为响应式的数据
  21. 使用方法:
  22. ```vue
  23. //(1)先引用ref
  24. import {ref} from 'vue';
  25. //(2)将数据变成响应式的。
  26. let data1=ref(12);
  27. //(3)操作数据
  28. data1.value = 123;
  29. ```
  30. ### reactive
  31. 定义对象格式的响应式数据
  32. 如果使用了 ref,内部会自动将对象/数据转换为 reactive 代理器对象
  33. ### toRefs
  34. 将响应式对象中所有属性包装为 ref 对象,并返回包含这些 ref 对象的普通对象。
  35. 应用:对 trsctive 定义的对象进行 toRefs 包装,包装之后的对象中每个属性都是响应式的。
  36. ### 响应式原理
  37. 通过 proxy(代理):拦截对对象本身的操作,包括属性的读写、删除等操作。
  38. 通过 Reflect(反射):动态对被代理对象的响应式属性进行特定的操作。
  39. ### watch 和 watchEffect
  40. #### watch - 指定监听数据
  41. - 监听指定的一个或多个响应式数据,一旦发生变化,就会自动执行监视回调。
  42. - 如果是监听 reactive 对象中的属性,必须通过函数来指定。
  43. - 监听多个数据,使用数组来指定。
  44. - 默认初始时不指定回调,但是通弄过配置 immediate 为 true,来指定初始时立即执行第一次。
  45. - 通过配置 deep 为 true,来指定深度监视。
  46. #### watchEffect - 不指定监听数据
  47. - 不用直接指定啦监视的数据,回调函数中使用的哪些响应式数据就监听哪些响应式数据。
  48. - 默认初始就会执行一次。
  49. ### 生命周期
  50. onMounted、onUpdated 和 onUnmounted等
  51. ![组件生命周期图示](https://cn.vuejs.org/assets/lifecycle_zh-CN.W0MNXI0C.png)
  52. ### ref获取元素
  53. > vue2中是用this ref.xxx来获取元素或组件,但是vue3中没有this的概念。
  54. > vue3通过ref创建响应式数据的api来获取元素。
  55. 1.使用ref创建响应式数据,假设叫x
  56. 2.模板中绑定ref属性,值为上面的x
  57. 注意不能使用v-bind动态绑定。
  58. 这时 x 就是一个dom元素或组件了。
  59. ```vur
  60. <!DOCTYPE html>
  61. <html lang="zh-CN">
  62. <head>
  63. <meta charset="UTF-8">
  64. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  65. <title>Vue 3 ref 引用示例</title>
  66. <script src="https://cdn.tailwindcss.com"></script>
  67. <link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
  68. <script src="https://cdn.jsdelivr.net/npm/vue@3/dist/vue.global.prod.js"></script>
  69. </head>
  70. <body class="bg-gray-50 min-h-screen">
  71. <div id="app" class="container mx-auto p-4 max-w-3xl">
  72. <div class="bg-white rounded-lg shadow-md p-6 mt-8">
  73. <h1 class="text-2xl font-bold text-gray-800 mb-6">Vue 3 ref 引用示例</h1>
  74. <div class="mb-6">
  75. <button @click="focusInput" class="bg-blue-500 hover:bg-blue-600 text-white font-bold py-2 px-4 rounded">
  76. <i class="fa fa-hand-pointer-o mr-2"></i>聚焦输入框
  77. </button>
  78. <button @click="changeText" class="bg-green-500 hover:bg-green-600 text-white font-bold py-2 px-4 rounded ml-2">
  79. <i class="fa fa-pencil mr-2"></i>修改文本
  80. </button>
  81. </div>
  82. <!-- 绑定 ref="x" 到输入框 -->
  83. <div class="mb-6">
  84. <label for="exampleInput" class="block text-gray-700 text-sm font-bold mb-2">普通输入框</label>
  85. <input type="text" id="exampleInput" ref="x" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent" placeholder="这是一个示例输入框">
  86. </div>
  87. <!-- 绑定 ref="x" 到自定义组件 -->
  88. <div class="mb-6">
  89. <label class="block text-gray-700 text-sm font-bold mb-2">自定义组件</label>
  90. <custom-component ref="x"></custom-component>
  91. </div>
  92. <div class="bg-gray-100 p-4 rounded-md">
  93. <h3 class="font-semibold text-gray-700 mb-2">状态信息</h3>
  94. <p class="text-gray-600"><i class="fa fa-info-circle mr-2"></i><span v-if="elementInfo">当前引用的元素: {{ elementInfo }}</span><span v-else>未选择元素</span></p>
  95. </div>
  96. </div>
  97. </div>
  98. <script>
  99. const { createApp, ref, onMounted } = Vue;
  100. // 自定义组件
  101. const CustomComponent = {
  102. template: `
  103. <div class="p-4 border border-gray-300 rounded-md bg-gray-50">
  104. <p class="text-gray-700">这是一个自定义组件</p>
  105. <button @click="doSomething" class="mt-2 bg-purple-500 hover:bg-purple-600 text-white px-3 py-1 rounded text-sm">
  106. <i class="fa fa-cog mr-1"></i>组件操作
  107. </button>
  108. </div>
  109. `,
  110. methods: {
  111. doSomething() {
  112. alert('自定义组件被点击了!');
  113. },
  114. changeText() {
  115. this.$el.querySelector('p').textContent = '文本已被修改';
  116. }
  117. }
  118. };
  119. const app = createApp({
  120. components: {
  121. CustomComponent
  122. },
  123. setup() {
  124. // 1. 使用 ref 创建响应式数据 x
  125. const x = ref(null);
  126. const elementInfo = ref('');
  127. // 组件挂载后执行
  128. onMounted(() => {
  129. updateElementInfo();
  130. });
  131. // 聚焦输入框方法
  132. const focusInput = () => {
  133. if (x.value) {
  134. x.value.focus();
  135. updateElementInfo();
  136. }
  137. };
  138. // 修改文本方法
  139. const changeText = () => {
  140. if (x.value) {
  141. if (x.value.tagName === 'INPUT') {
  142. x.value.value = '文本已被修改';
  143. } else if (typeof x.value.changeText === 'function') {
  144. x.value.changeText();
  145. }
  146. updateElementInfo();
  147. }
  148. };
  149. // 更新元素信息
  150. const updateElementInfo = () => {
  151. if (x.value) {
  152. if (x.value.tagName) {
  153. elementInfo.value = `HTML 元素 - ${x.value.tagName.toLowerCase()}`;
  154. } else {
  155. elementInfo.value = '自定义组件实例';
  156. }
  157. }
  158. };
  159. return {
  160. x,
  161. focusInput,
  162. changeText,
  163. elementInfo
  164. };
  165. }
  166. });
  167. app.mount('#app');
  168. </script>
  169. </body>
  170. </html>
  171. ```
  172. ### 自定义hook函数
  173. > hook函数翻译成中文就是钩子函数(注意并不是生命周期的钩子函数)
  174. > 比如ref,reactive,computed,watch,onBeforeMount等都是hook函数,只不过他们都是vue内部hook函数。
  175. 1.创建一个函数,函数名称必须以"use"开头
  176. 2.函数必须return一些数据。
  177. `export function useFetchData()`
  178. ### shallowReactive与shallowRef
  179. 他们都表示浅响应式。
  180. - shallowReactive:只处理了对象第一层属性的响应式(值响应第一层)
  181. - shallowRef:只有重新复制时才是响应式(不响应内部数据,只响应整体。)
  182. ### -readonly与shallowReadonly
  183. - 他们表示只读代理对象
  184. - readonly
  185. - 深度只读
  186. - 设置readonly后,修改响应式数据会报错。
  187. - shalloReadonly
  188. - 浅只读
  189. - 设置shalloReadonly后,修改响应式数据的第一层数据会报错。
  190. - 应用场景:
  191. - 在某些特定情况下,我们可能不希望对数据进行更新的操作,那就可以包装成一个只读代理对象,而不能修改或删除。
  192. ### -toRaw与markRaw
  193. - toRaw
  194. - 返回reactive或readonly对象的原始数据
  195. - 这是一个还原方法,可用于临时读取,得到的数据不具有响应式。
  196. - markRow:
  197. - 标记一个对象,使其不具有响应式
  198. - 应用场景:
  199. - 有些只不应被设置为响应式的,例如复杂的第三方实例或Vue组件对象。
  200. - 当渲染具有不可变数据源的大列表时,跳过代理转换可以提高性能。
  201. ### toRef
  202. - 为响应式对象上的某个属性创建一个ref引用,更新是应用对象会同步更新。
  203. - 与ref的区别:ref是拷贝了一份新的数据指单独操作,更新时相互不影响。
  204. ### -customRef
  205. - 用于自定义一个ref,可以显示的控制依赖追踪和触发相应。
  206. - 接受一个工厂函数,两个参数分别用于追踪的track与用于触发相应的trigger,并方法一个带有get和set属性的对象。
  207. - 需求:使用customRef实现防抖函数
  208. ### -provide与inject
  209. - provide和inject提供依赖注入,功能类似2.0的provide/inject
  210. - 实现跨层级组件(祖孙)间通信。
  211. ### -响应式数据的判断
  212. - isRef:检查一个值是否为一个ref对象。
  213. - isReactive:检查一个对象是否否是由reactive对象的响应式代理。
  214. - isReadonly:检查一个对象是否由readonly创建的只读代理。
  215. - isProxy:检查一个对象是否是由reactive或者readonly方法创建的代理。
  216. ### Fragment
  217. - 在vue2中:组件中必须有一个跟标签
  218. - 在vue3中:组价可以没有跟标签,内部会将多个标签包含在一个Fragment虚拟标签中。
  219. - 好处:减少标签层级,减小内存占用
  220. ### Teleport
  221. - Teleport提供了一种干净的方法,让组件的HTML在父组件界面外的特定标签(很可能是body)下插入显示。
  222. ### Suspense
  223. > Supense组件是配合一部组件使用的,它可以让一部组件返回数据前渲染一些后背内容。
  224. > 那我们首先要学会一个异步组件。
  225. - 在setup函数总返回一个promise,就是一个异步组件。
  226. - setup函数携程async函数,也是一个异步组件。
  227. ### 其他新的API
  228. - 全新的全局API:
  229. - createApp()
  230. - defineProperty()
  231. - defineComponent()
  232. - nextTick()
  233. - 将原来的全局API转移到应用对象:
  234. - app.component()
  235. - app.config()
  236. - app.directive()
  237. - app.mount()
  238. - app.umount()
  239. - app.use()
  240. ### useSlots和useAttrs
  241. > useSlots 和 useAttrs 是真实的运行时函数,它会返回与 setupContext.slots 和 setupContext.attrs 等价的值,同样也能在普通的组合式 API 中使用。
  242. > 使用场景:父组件使用子组件的插槽
  243. 1.父组件
  244. ```xml
  245. <template>
  246. <h1>这是父组件</h1>
  247. <p>插槽上面1</p>
  248. <slots-attrs msg="我是props的msg" heihei="我是attr">
  249. <template #header >
  250. <div style="width:100%;height:100px;border:1px solid green;">我是父组件插槽--插入的内容。。。</div>
  251. </template>
  252. </slots-attrs>
  253. <p>插槽下面2</p>
  254. </template>
  255. <script lang="ts" setup>
  256. import SlotsAttrs from '@/components/04/SlotsAttrs.vue'
  257. </script>
  258. ```
  259. 2.子组件
  260. ```xml
  261. <template>
  262. <Child ref="child"/>
  263. {{msg}}
  264. </template>
  265. <script lang="ts" setup>
  266. import { ref,onMounted } from 'vue';
  267. import Child from '@/components/03/Child.vue'
  268. const child = ref(null);
  269. const msg=ref('')
  270. onMounted(()=>{
  271. console.log("进来了")
  272. console.log(child.value.msg)
  273. msg.value = child.value.msg;
  274. })
  275. </script>
  276. ```
  277. ## 路由
  278. (1)userRout:获得当前路由对象。
  279. (2)useRouter:获得路由实例,可以进行路由跳转。
  280. ### 使用
  281. ```javascript
  282. import {useRoute,useRouter} from "vue-router"
  283. ```
  284. ## 实战
  285. 创建项目后
  286. ### ElementPlus
  287. 引入
  288. ```sh
  289. npm install element-plus --save
  290. ```
  291. 尝试静态界面
  292. ![image-20250618172046133](assets/Vue3/image-20250618172046133.png)