SFC 语法规范
*.vue 件都由三种类型的顶层语法块所组成:<template>、<script>、<style>
<template>
- 每个
*.vue文件最多可同时包含一个顶层<template>块。 - 其中的内容会被提取出来并传递给
@vue/compiler-dom,预编译为JavaScript的渲染函数,并附属到导出的组件上作为其render选项。
<script>
- 每一个
*.vue文件可以有多个 )。 - 该脚本将作为
ES Module来执行。 - 其默认导出的内容应该是
Vue组件选项对象,它要么是一个普通的对象,要么是 defineComponent 的返回值。
<script setup>
- 每个
*.vue文件最多只能有一个<script setup>块 (不包括常规的 - 该脚本会被预处理并作为组件的 setup() 函数使用,也就是说它会在每个组件实例中执行。
<style>
- 一个
*.vue文件可以包含多个<style>标签。 <style>标签可以通过scoped或module attribute(更多详情请查看 SFC 样式特性) 将样式封装在当前组件内。多个不同封装模式的<style>标签可以在同一个组件中混
未编译模板闪烁
使用v-cloak
<style>
[v-cloak]{
display:none;
}
</style>
<template>
<div id="#app" v-cloak>{{ msg }} </div>
</template>
避免使用 v-html
在网站上动态渲染任意 HTML 是非常危险的,因为这非常容易造成 XSS 漏洞。请仅在内容安全可信时再使用 v-html,并且永远不要使用用户提供的 HTML 内容(script也属于HTML内容)。
属性绑定 v-bind:attrName / :attrName
如果绑定的值是 null 或者 undefined,那么该 attribute 将会从渲染的元素上移除。
事件修饰符
事件修饰符的先后顺序不同,执行的效果也会有所不同
条件渲染
一个 v-else 元素必须跟在一个 v-if 或者 v-else-if 元素后面,否则它将不会被识别。
想要切换不止一个元素,在这种情况下我们可以在一个 <template>元素上使用 v-if,这只是一个不可见的包装器元素,最后渲染的结果并不会包含这个 <template> 元素。==>template是一个空标签
不同之处在于 v-show 会在 DOM 渲染中保留该元素;v-show 仅切换了该元素上名为 display 的 CSS 属性。
v-show 不支持在 <template> 元素上使用,也不能和 v-else 搭配使用。
列表渲染
v-for渲染时要加:key= 唯一值,这里的唯一值一般为后端传来的id即可,若v-for与v-if作用于同一个标签上,在v2中,v-for的优先级比v-if要高,v3则相反.
强烈不建议放在同一标签上!!!
表单输入绑定
v-model 会忽略任何表单元素上初始的 value、checked 或selected 属性。它将始终将当前绑定的 JavaScript 状态视为数据的正确来源。你应该在 JavaScript 中使用data 选项来声明该初始值。
多选框建议值设置为数组,开关建议值设置为boolean类型.
计算属性computed
计算属性默认仅能通过计算函数得出结果。当你尝试修改一个计算属性时,你会收到一个运行时警告。只在某些特殊场景中你可能才需要用到“可写”的属性,你可以通过同时提供 getter 和 setter 来创建
computed:{
reverseMsg(){
// 警告warn :readonly ! ! !
return this.msg.split('').reverse().join('')
}
}
computed:{
fullName:{
get(){
return this.firstName + ' ' + this.lastName
},
set(val){
this.firstName = newValue.split(' ')[0]
this.lastName = newValue.split(' ')[1]
}
}
}
侦听器watch
使用场景:数据变化时执行异步或开销比较大的操作。
watch默认是浅层响应,仅会监听第一层,若想监听深层数据,可用配置项deep:true.若刚开始就想监听数据,可以使用immediate:true.若想达到某一个条件下再开启监听,需要使用 this.$watch(监听的数据,callback)手动去添加侦听器.
如果不使用深度侦听,如何监听对象下的属性的变化?可以通过 监听 对象.属性的变化
总的来说, watch用于监听数据的变化并执行自定义操作,而 computed用于计算基于依赖数据的属性.
vue3组合式API中的watch第一个参数默认只能为对象,若需要监听某个值可以写为 [arg1,arg2] 或 (=>obj.keyName) 这种写法
单向数据流
父组件通过props传递给子组件的数据,子组件无法对其进行更改,称之为单向数据流.
注意在 JavaScript 中对象和数组是引用类型,指向同一个内存空间,如果 prop 是一个对象或数组,在子组件内部改变它会影响父组件的状态。
属性透传
父组件内子组件标签的属性,默认会透传到子组件的根元素上,可用props来接收或设置inheirtAttrs:false,借助v-bind="$attrs"可指定透传到子组件的某个标签上
<template id="button">
<!-- Attributes 继承(class, v-on事件继承) -->
<div class="button">
<div >测试</div>
<!--借助v-bind指令可以让透传的属性放在button中-->
<button class="btn" v-bind="$attrs">按钮</button>
</div>
</template>
router-link的active-class
默认情况下,Vue Router的路由匹配是基于前缀的,所以如果当前路由是/,它将匹配所有以/开头的路由。通过添加exact属性,我们确保只有当路由完全匹配时才应用active-class。
<template>
<div id="app">
<nav>
<router-link to="/" exact active-class="active">Home</router-link>
<router-link to="/about" exact active-class="active">About</router-link>
<router-link to="/mine" exact active-class="active">Mine</router-link>
</nav>
<router-view />
</div>
</template>
<style>
.active {
font-weight: bold;
color: red;
}
</style>
动态组件 :is
使用<component :is="templateName">指定组件,如果想让组件保持存活状态,可以使用<keepAlive>,默认为全部缓存, 且任何时候都只有一个活跃组件示例作为<keepAlive>的直接子节点.
当一个组件在 中被切换时,它的 activated 和 deactivated 生命周期钩子将被调用,用来替代 mounted 和 unmounted。
可使用include / exclude制定组件中的name来确定要被缓存的组件,组件如果想要条件性地被KeepAlive 缓存,就必须显式声明一个 name 选项。
DOM 模板解析注意事项(is="vue:组件名")
若模板标签为tr,但是,在这种用例中,你可能需要 Vue 用其组件来替换原生元素,你可以在 is 的值中加上 vue: 前缀,这样 Vue 就会把该元素渲染为 Vue 组件(my-row-component为自定义组件):
<table>
<tr is="vue:my-row-component"></tr>
</table>
依赖注入
provide的数据不会保持数据响应性,可使用computed(callback)的方式来保持数据响应性.
count: computed(() => this.count) // 响应式关键
如果我们想要用一个不同的本地属性名注入该属性,我们需要在 inject 选项的属性上使用对象的形式:
{
inject: {
/* 本地属性名 */ localMessage: {
from: /* 注入来源名 */ 'message'
}
}
}
这里,组件本地化了原注入名 message 所提供的的属性,并将其暴露为this.localMessage。
默认情况下,inject假设传入的注入名会被某个祖先链上的组件提供。如果该注入名的确没有任何组件提供,则会抛出一个运行时警告。
如果在注入一个值时不要求必须有提供者,那么我们应该声明一个默认值,和 props 类似:
{
// 当声明注入的默认值时,必须使用对象形式
inject: {
message: {
from: 'message', // 当与原注入名同名时,这个属性是可选的
default: 'default value'
},
user: {
// 对于非基础类型数据,如果创建开销比较大,或是需要确保每个组件实例
// 需要独立数据的,请使用工厂函数
default: () => ({ name: 'John' })
}
}
}
混入Mixins
除了生命周期钩子函数外,所有的代码都以组件为优先,生命周期钩子函数先执行混入的,后执行组件的.
尽量避免全局混入的使用!!!
路由未匹配路径
{
path: '/404',
name: '404',
component: () => import('@/view/NotFound.vue')
},
{
path: '/:catchAll(.*)*',
redirect: '/404'
}
路由守卫
路由守卫方法都需要进行next放行或return
全局路由守卫
路由访问之前 beforeEach(to,from,next)
const routers = createRouter({
history: createWebHashHistory(),
routes
})
routers.beforeEach(to,from,next){
}
路由访问之后 afterEach(to,from,next)
const routers = createRouter({
history: createWebHashHistory(),
routes
})
routers.afterEach(to,from,next){
}
局部路由钩子
局部路由访问之前 beforeEnter
beforeEnter(to,from,next){
next('routerPath')
}
局部组件路由钩子
组件路由访问之前 beforeRouterEnter
beforeRouterEnter(to,from,next)
组件路由改变时 beforeRouterUpdate
beforeRouterUpdate(to,from,next)
组件路由组件访问之后 beforeRouterLeave
beforeRouterLeave(to,from,next)
meta
<!-- 路由组件中: -->
meta:{
keyName:value
}
// view层:
$route.meta.keyName
vuex
module写法需要在状态模块中添加配置项
{
namespaced:true
}
辅助函数
import{mapState,mapGetters,mapMutations,mapActions} from 'vuex'
mapState / mapGetters 均为一个对象,需要用扩展运算符 ... 在computed中展开
mapState
与组件的data()类似,用来存储状态标识
computed:{
...mapState('moduleName',['stateKeyName','stateKeyName2'])
}
mapGetters
与组件的computed计算属性类似
computed:{
...mapGetters('moduleName',['gettersFunctionName','gettersFunctionName2'])
}
mapMutations / mapActions 均为一个对象,需要用扩展运算符 ... 在methods中展开
mapMutations
用来管理同步状态变化
methods:{
...mapMutations('moduleName',['mutationsFunctionName','mutationsFunctionName2'])
}
mapActions
mapActions 提交commit的是 mapMutations中的mutationsFunctionName ,而不是直接变更状态。
用来管理异步状态变化
methods:{
...mapActions('moduleName',['actionsFunctionName','actionsFunctionName2'])
}
css样式覆盖问题
若组件嵌套层次过深,可使用:deep(css选择器)进行修改
body{
:deep(p,span,.class,#id){
color:red
}
}