共计 14925 个字符,预计需要花费 38 分钟才能阅读完成。
迁移指南:https://v3.cn.vuejs.org/guide/migration/introduction.html
好用的插件:https://vueuse.org/,被遗忘了的几个指令 v-pre、v-once、this.$forceUpdate()、this.nextTick()
编程总结:在 setup 中应该如何去规划代码?如何模块化?如何才不会一团乱?
vue3 不同构建版本
Vue3 中不再构建 UMD 模块化的方式,因为 UMD 会让代码有更多的冗余,它要支持多种模块化的方式。
Vue3 中将 CJS、ESModule 和自执行函数的方式分别打包到了不同的文件中。在 packages/vue 中有 Vue3 的不同构建版本。
相关说明:https://cn.vuejs.org/v2/guide/installation.html
1.cjs
两个版本都是完整版,包含编译器
vue.cjs.js、vue.cjs.prod.js(开发版,代码进行了压缩)
2.global
这四个版本都可以在浏览器中直接通过 scripts 标签导入,导入之后会增加一个全局的 Vue 对象
vue.global.js(完整版,包含编译器和运行时)
vue.global.prod.js(完整版,包含编译器和运行时,这是开发版本,代码进行了压缩)
vue.runtime.global.js
vue.runtime.global.prod.js
3.browser
四个版本都包含 esm,浏览器的原生模块化方式,可以直接通可以直接通过 的方式来导入模块
vue.esm-browser.js
vue.esm-browser.prod.js
vue.runtime.esm-browser.js
vue.runtime.esm-browser.prod.js
4.bundler
这两个版本没有打包所有的代码,只会打包使用的代码,需要配合打包工具来使用,会让 Vue 体积更小
vue.esm-bundler.js
bue.runtime.esm-bundler.js
setup 组件选项
在 setup 中你应该避免使用 this,因为它不会找到组件实例。setup 的调用发生在 data property、computed property 或 methods 被解析之前,所以它们无法在 setup 中被获取。
setup 选项是一个接收 props 和 context 的函数,setup 返回的所有内容都暴露给组件的其余部分 (计算属性、方法、生命周期钩子等等) 以及组件的模板。
setup 的执行在 beforeCreate 之前。只执行一次(参数都是包装后的 proxy 对象)
- props,代表给组件传递的参数
- context,组件所处的上下文对象(props、emit、slots);
思考
在 setup 如何高效的、准确的把各种逻辑抽离出来?使用组合式 API 时,在实践中尝试 MVC,尽量不要把主要的业务逻辑写在组件里。setup 只是为 组件载入逻辑
提供了一个入口,而不应该把所有东西都写在里面。
Vue3 Composition API 中的提取和重用逻辑:https://blog.csdn.net/duninet/article/details/105716706
1.inheritAttrs
默认情况下父作用域的不被认作 props 的 attribute 绑定 (attribute bindings) 将会“回退”且作为普通的 HTML attribute 应用在子组件的根元素上。当撰写包裹一个目标元素或另一个组件的组件时,这可能不会总是符合预期行为。通过设置 inheritAttrs 到 false,这些默认行为将会被去掉。而通过实例 property $attrs 可以让这些 attribute 生效,且可以通过 v-bind 显性的绑定到非根元素上。
该 property 包括组件 props 和 emits property 中未包含的所有属性 (例如,class、style、v-on 监听器等)。
与单个根节点组件不同,具有多个根节点的组件不具有自动 attribute fallthrough (隐式贯穿) 行为。如果未显式绑定 $attrs,将发出运行时警告。
2.compilerOptions
compilerOptions 用于设置编译模板的时候的一些配置
const Foo = {
// ...
compilerOptions: {delimiters: ['${', '}'],
comments: true
}
}
组件中的 style 样式
1. 深度选择器:deep()
处于 scoped 样式中的选择器如果想要做更“深度”的选择,也即:影响到子组件,可以使用 :deep() 这个伪类;
2. 插槽选择器:slotted()
默认情况下,作用域样式不会影响到 渲染出来的内容,因为它们被认为是父组件所持有并传递进来的。使用 :slotted 伪类以确切地将插槽内容作为选择器的目标:
3. 全局选择器:global(.red)
如果想让其中一个样式规则应用到全局,比起另外创建一个
4.css module
实际的值会被编译成 hash 的 CSS 自定义 property,CSS 本身仍然是静态的。自定义 property 会通过内联样式的方式应用到组件的根元素上,并且在源值变更的时候响应式更新。
低级静态组件与 v-once
在 Vue 中渲染纯 HTML 元素的速度非常快,但有时你可能有一个包含很多静态内容的组件。在这些情况下,可以通过向根元素添加 v-once 指令来确保只对其求值一次,然后进行缓存,如下所示:
app.component('terms-of-service', {
template: `
Terms of Service
... a lot of static content ...
`
})
提示
再次提醒,不要过度使用这种模式。虽然在需要渲染大量静态内容的极少数情况下使用这种模式会很方便,但除非你注意到先前的渲染速度很慢,否则就没有必要这样做——另外,过度使用这种模式可能会在以后引起很多混乱。例如,假设另一个开发人员不熟悉 v-once 或者没有在模板中发现它,他们可能会花上几个小时来弄清楚为什么模板没有正确更新。
异步组件
在大型应用中,我们可能需要将应用分割成小一些的代码块,并且只在需要的时候才从服务器加载一个模块。为了实现这个效果,Vue 有一个 defineAsyncComponent 方法
官方文档:https://v3.cn.vuejs.org/guide/component-dynamic-async.html#%E5%BC%82%E6%AD%A5%E7%BB%84%E4%BB%B6
import {defineAsyncComponent} from 'vue'
const AsyncComp = defineAsyncComponent(() =>
import('./components/AsyncComponent.vue')
)
app.component('async-component', AsyncComp)
异步组件可以搭配 vite 的 import.meta.glob,批量引入某个目录下的所有组件作为异步组件。
问题总结
1. 直接在浏览器内通过 script 引入 Vue3,需要注意以下几点
- setup 选项内,不能像在 Cli 内一样使用 Vue 的 APi(如 ref、reactive 等),需要使用 Vue.ref 的形式才能调用。
- 闭包指的是在函数内定义的函数,所以它能直接使用上一个函数内的所有数据对象,而普通函数被调用时,是无法使用上一个执行的函数的局部变量的。在浏览器环境下需要将 ref、reactive 等 API 注册为全局变量。从而实现在 setup 内的模块化。
- 在浏览器环境下,组件标签必须正常闭合,否则会导致模板解析错误。
- 使用 Vant 库时,例如 loading 这些 API,因为无法使用 this 调用 vue 实例,所以在 setup 内需要通过 vant 对象去调用。(CLI 下通过 Use 引入入的 Toast 对象)
- v-slot:slotName,指定插槽名(只能在 template 标签上使用,只有一种特殊情况),v-slot 也有缩写,即把参数之前的所有内容 (v-slot:) 替换为字符 #。
2.setup 异步请求
在开发 vue3 中,因为通过接口数据为异步函数获取,导致最后数据无法成功赋值进 return 中的数据。所以需要 setup 函数异步转同步,后设置了 async 后异步转同步,结果导致页面空白不显示。在 Vue3 中,如果当前组件的 setup 使用了 async/await,那么其调用组件的父组件(父组件中引用 defineAsyncComponent 定义异步组件)的外层需要嵌套一个 suspense 标签
异步组件不需要作为 的直接子节点。它可以出现在组件树任意深度的位置,且不需要出现在和 自身相同的模板中。只有所有的后代组件都准备就绪,该内容才会被认为解析完毕。
3.Vue.use 必须在 Vue.mount 之前
Vue.use 必须在 Vue.mount 之前,否则会报错。use is not a fucntion.
4.vant 自动引入样式出错时
将安装的 vite-plugin-style-import 插件 1 .2.0 版本改为 1.4.1版本即可
npm install vite-plugin-style-import@1.4.1
5. 在 setup 中操作 dom 节点
业务场景:echarts 在指定的 div 上画图。
/* 示例模板 */
/* 定义一个 ref*/
let dom=ref(null)
/* 这里打印的就是节点 */
onMounted(()=>{console.log(dom.value);
})
6.async setup 中注册生命周期钩子时,必须写在第一个 await 之前;以下来自源代码的提示:
onMounted is called when there is no active component instance to be associated with.
Lifecycle injection APIs can only be used during execution of setup().
If you are using async setup(), make sure to register lifecycle hooks before the first await statement.
7.setup 自定义事件注册与触发
定义可接收的自定义事件:https://v3.cn.vuejs.org/api/sfc-script-setup.html#defineprops-%E5%92%8C-defineemits
使用 setup 配置项时,通过 emits 配置项定义。
触发自定义事件:
- 配置项 setup 中通过 setup 函数的参数 context.emit 去触发。
- 语法糖 script setup 中通过 difineEmit 返回的对象去触发
/* 传递 context,用于触发自定义事件 */
onMounted(()=>{mounted(context);
}); // 挂载生命周期
8. 传递的 props 不是响应式的
传递的 props 不建议去修改,基础类型和对象的引用修改时都会报错,传递的 props 值是一个对象时,属性值是可以修改的。
provide/inject 可以看作是“长距离的 prop”,默认情况下,provide/inject 绑定并不是响应式的。我们可以通过传递一个 ref property 或 reactive 对象给 provide 来改变这种行为。
9.object
object["prop"] 和 object.prop 是通用的。
10.script setup 内接受传参和自定义事件
/* 定义注册自定义事件,设置模块的显示 */
let emit=defineEmits(["set"])
/* 定义接受模块的设置 */
let props=defineProps(["modules"])
11.transition 和 transition-group
transition:用于给单独一个组件定义动画
transition-group:用于给多个组件同时定义动画
12. 动态组件
13. 通过 props 传递一个响应式数据
传递的 props 属性,对于基础类型和对象的引用修改时都会报错,但是修改对象的值是可以的,并且父组件会保持对这个属性的响应。
14. 整个对象的替换,保持响应式
不管是 vue2 还是 3,对于响应式对象的替换和修改都只能在对象内部进行,而不能直接去替换这一整个响应式对象,例如 vue2 中 data 返回的对象,直接替换之后就成为一个普通对象了,vue 根本没机会去执行 Object.defineProperty,对于 Proxy 对象,同样是如此。所有要想替换一整个对象,只能用 Proxy 对象的一个属性去进行操作。
15.vue 绑定事件时传递事件对象
有时也需要在内联语句处理器中访问原始的 DOM 事件。可以用特殊变量 $event
把它传入方法
16.setup 内的异步操作
provide、inject、生命周期钩子都需要在异步操作之前,不然会导致获取不到值或者无法正常执行。
17.script setup 内使用动态组件
不同于之前的直接使用字符串的组件名,在 script setup 中使用动态组件 is 必须是一个代表引入组件的变量名,假如使用 record 组件(通过 import 引入),作为动态组件时必须把组件变量作为 is 的属性值。
/* 通时通次 */
import record from './workspace/record.vue'
/* 将引入的组件作为变量 */
let component={record,add,achievement,enter,sign,effective};
/*is 值为引入的组件配置对象 */
18. 异步数据加载(suspense)
- 使用 suspense 和 await。await 等待期间显示 suspense 的加载效果。
- 通过一个加载状态的标志,异步请求结束后变更为加载完成,显示主内容,未加载完时显示一个加载效果。
- 主要是要搞清楚,如果必须要同步那就 await,不需要的话就可以使用加载标志。
- 什么时候需要使用 await 操作,那就是有多个异步行为的时候,后一个异步依赖于前一个异步的结果,可以避免大量的回调操作
/* 获取各种排名数据 */
let rank=ranks();
场景举例
如上,ranks 内有一个异步请求,按照 js 的运行逻辑,不会等待请求完毕,而是继续往下运行,所以最终 rank 为 undefine;那么该如何解决,一是使用 await 同步执行,而是返回一个响应式的变量,让异步更新时,同步数据值。使用异步组件时,同样适用于此。
19.keep-alive 理解
当组件在
- avtived 和 deactived,在 keeplive 内任意一个组件注册时,路由组件从缓存中被激活、隐藏时触发。
- vue-router 是基于动态组件实现的。
- keep-alive 不支持多级路由缓存,对于一个单独的层级可以单独定义 keep-alive。
- include - string | RegExp | Array。只有名称匹配的组件会被缓存。
- exclude - string | RegExp | Array。任何名称匹配的组件都不会被缓存。
- max - number | string。最多可以缓存多少组件实例。
官方文档:https://v3.cn.vuejs.org/guide/component-dynamic-async.html
18. 模板相关知识
模板不仅可以使用 data 等响应式数据,也可以直接使用 $route 等 api;
19.vue 记录一次监视属性
通过 watch 监视一整个对象,对象和表单双向绑定。对象是通过 axios 从后端请求过来的,后赋值到 reactive 对象的属性(注意:此后这个数据对象、watch 返回的 new、old 值都是这个对象的引用)。因为存在 null 值,绑定到表单的时候 null 会被转换为空字符串,导致对象属性发送改变,触发一次 watch。在模板渲染和监视之前将 null 批量转换为空字符串,从而避免触发 watch
null2str(data) {if (typeof data != 'object'|| data === null|| data ==='null') {
data = '';
return data;
}else{for (let x in data) {if (data[x] === null || data[x] === 'null') { /* 如果是 null 把直接内容转为 ''*/
data[x] = '';
} else {if (Array.isArray(data[x])) { /* 是数组遍历数组 递归继续处理 */
data[x] = data[x].map(z => {console.log(z)
return null2str(z);
});
}
if(typeof(data[x]) === 'object'){ /* 是 json 递归继续处理 */
data[x] = null2str(data[x])
}
}
}
return data;
}
}
20. v-model 默认属性和事件的变化
官方文档:https://v3.cn.vuejs.org/guide/migration/v-model.html#%E6%A6%82%E8%A7%88
用于自定义组件时,v-model prop 和事件默认名称已更改:
- prop:value -> modelValue(model-value);
- 事件:input -> update:modelValue;
21. 父组件操作子组件
在父组件中可以通过子组件的实例对象,调用子组件内的方法。
使用
37.transition
是一个内置组件,这意味着它在任意别的组件中都可以被使用,无需注册。它可以将进入和离开动画应用到通过默认插槽传递给它的元素或组件上。
- 由 v-if 所触发的切换
- 由 v-show 所触发的切换
- 由特殊元素 切换的动态组件
触发过程:
- v-enter-from:元素插入或显示之前添加,插入或显示后的下一帧移除;
- v-enter-active:元素插入或显示之前添加,在过渡或动画完成之后移除。这个 class 可以被用来定义进入动画的持续时间、延迟与速度曲线类型。
- v-enter-to:在元素插入完成后的下一帧被添加 (也就是 v-enter-from 被移除的同时),在过渡或动画完成之后移除。
- v-leave-from:离开动画的起始状态。在离开过渡效果被触发时立即添加,在一帧后被移除。
- v-leave-active:离开动画的生效状态。应用于整个离开动画阶段。在离开过渡效果被触发时立即添加,在过渡或动画完成之后移除。这个 class 可以被用来定义离开动画的持续时间、延迟与速度曲线类型。
- v-leave-to:离开动画的结束状态。在一个离开动画被触发后的下一帧被添加 (也就是 v-leave-from 被移除的同时),在过渡或动画完成之后移除。
初始状态(enter-from)-> 定义动画或过渡的属性(v-enter-active)-> 触发动画或过渡(v-enter-to)-> 全部移除
38. css v-bind 不生效
在 style 标签内进行 v -bind 绑定时,遇到了绑定不生效的问题,研究了之后发现通过 v -bind 绑定的属性是作为组件根节点上 style 的属性值进行绑定的,所有只能给组件内部或者子组件使用(不能进行穿透?)。
39.vue 绑定全局方法
// 之前 - Vue 2
Vue.prototype.$http = () => {}
// 之后 - Vue 3
const app = createApp({})
app.config.globalProperties.$http = () => {} ```