watchEffect
,它立即执行传入的一个函数,同时响应式追踪其依赖,并在其依赖变更时重新运行该函数。
换句话说:watchEffect
相当于将watch
的依赖源和回调函数合并,当任何你有用到的响应式依赖更新时,该回调函数便会重新执行。不同于 watch
,watchEffect
的回调函数会被立即执行(即 { immediate: true }
)
watchEffect
的回调函数就是一个副作用函数,因为我们使用watchEffect
就是侦听到依赖的变化后执行某些操作。
import { watchEffect, ref } from 'vue'
const count = ref(0)
watchEffect((onInvalidate) => {
console.log(count.value)
onInvalidate(() => {
console.log('执行了onInvalidate')
})
})
setTimeout(()=> {
count.value++
}, 1000)
定义一个定时器,或者监听某个事件,我们需要在mounted
生命周期钩子函数内定义或者注册,然后组件销毁之前在beforeUnmount
钩子函数里清除定时器或取消监听。这样做我们的逻辑被分散在两个生命周期,不利于维护和阅读。
如果我利用watchEffect
,创造和销毁逻辑放在了一起
// 定时器注册和销毁
watchEffect((onInvalidate) => {
const timer = setInterval(()=> {
// ...
}, 1000)
onInvalidate(() => clearInterval(timer))
})
const handleClick = () => {
// ...
}
// dom的监听和取消监听
onMounted(()=>{
watchEffect((onInvalidate) => {
document.querySelector('.btn').addEventListener('click', handleClick, false)
onInvalidate(() => document.querySelector('.btn').removeEventListener('click', handleClick))
})
})
利用watchEffect做一个防抖节流
const id = ref(13)
watchEffect(onInvalidate => {
// 异步请求
const token = performAsyncOperation(id.value)
// 如果id频繁改变,会触发失效函数,取消之前的接口请求
onInvalidate(() => {
// id has changed or watcher is stopped.
// invalidate previously pending async operation
token.cancel()
})
})
可以使用 Vue.js 的三元运算符在 {{ }}
中展示不同的状态,具体代码如下:
<el-table-column label="状态" min-width="100">
{{row.status === 1 ? '已完成' : row.status === 2 ? '进行中' : '未开始'}}
</el-table-column>
在上面的代码中,我们使用了两个嵌套的三元运算符来实现根据 row.status
的值显示不同的状态。如果 row.status
的值为 1
,则显示 "已完成"
,如果值为 2
,则显示 "进行中"
,否则显示 "未开始"
。
在 Vue 3 中,可以使用 v-slot
的缩写语法(#
)来指定插槽的位置。对于 el-table-column
组件中的插槽,您可以将模板语法放置在 <template v-slot:default>
或 <template #default>
标签内。
下面是根据您提供的代码示例修改后的 Vue 3 代码,用于在 el-table-column
组件中显示 sysUser.active
字段的值:
<el-table-column label="状态" min-width="100">
<template #default="{ row }">{{ row.active }}</template>
</el-table-column>
在上面的代码中,我们使用了 #default
缩写语法来定义插槽,并使用对象解构来获取 row
参数。然后,我们在模板中使用 row.active
来显示 active
字段的值。
el-table-column
组件没有绑定数据源:请确保你的表格组件已经正确地绑定了数据源,例如使用 :data
属性绑定数据源。.active
字段不存在或为空值:请确保 .active
字段存在且有值。如果该字段不存在或为空,您的模板代码将无法正确地解析。要根据条件隐藏<el-form-item>
,可以使用Vue的条件渲染指令v-if或v-show。
使用v-if指令时,只有当条件为真时,元素才会被渲染到页面上。如果条件变为假,则该元素从DOM中删除。
例如, 如果您想要在表单项不为空时才显示密码字段,您可以将v-if指令添加到包装表单项的
元素中:
<div v-if="formData.name !== ''">
<el-form-item label="密码" prop="name">
<el-input
class="ls-input"
v-model="formData.passwd"
placeholder="请输入密码"
clearable
/>
</el-form-item>
</div>
在这个例子中,<el-form-item>
只有在formData.name
不为空时才会被渲染出来。
另一种选择是使用v-show指令。与v-if不同的是,当条件为假时,元素仍然存在于DOM中,只是样式设置为display:none。
例如,如果您希望在表单项不为空时显示密码字段,您可以将v-show指令添加到元素中:
<el-form-item label="密码" prop="name" v-show="formData.name !== ''">
<el-input
class="ls-input"
v-model="formData.passwd"
placeholder="请输入密码"
clearable
/>
</el-form-item>
在这个例子中,当formData.name
不为空时,<el-form-item>
将显示出来,否则它将以display:none的方式隐藏起来。
data | 展示数据 |
empty-text | 内容为空的时候展示的文本 |
node-key | 每个树节点用来作为唯一标识的属性,整棵树应该是唯一的 |
render-after-expand | 是否在第一次展开某个树节点后才渲染其子节点 |
load 加载子树数据的方法,仅当lazy属性为true时生效
render-content 树节点的内容区的渲染Function
highlight-current 是否高亮当前选中节点,默认值是false
default-expand-all 是否默认展开所有节点
expand-on-click-node
是否在点击节点的时候展开或者收缩节点, 默认值为 true,如果为 false,则只有点箭头图标的时候才会展开或者收缩节点。
这段代码使用了 Vue 3 Composition API 中的 defineProps
函数来定义一个名为 props
的响应式属性对象,并通过 withDefaults
函数设置了默认值。
首先,defineProps
函数是用于定义组件接收的 props 属性的函数。<TreeFilterProps>
泛型表示这个组件接收的 props 类型,即传递给组件的数据类型。这个函数返回一个 props 对象,其中每个属性都对应着组件中接收的 prop 属性。在这个例子中,这个 props 对象是空的,也就是说这个组件没有接收任何 props 属性。
接下来,withDefaults
函数是一个辅助函数,用于设置默认值。此函数接收两个参数:第一个是需要设置默认值的对象,第二个是一个包含默认值的对象。在这个例子中,我们将 defineProps<TreeFilterProps>()
返回的空对象作为第一个参数传入,表示我们需要给这个对象设置默认值;然后,我们将一个包含默认值的对象作为第二个参数传入,其中 id: 'id'
表示默认的 id
属性是 'id'
,label: 'label'
表示默认的 label
属性是 'label'
,multiple: false
表示默认的 multiple
属性是 false
。
因此,这段代码的作用是定义一个响应式的 props
属性对象,并为其设置默认值。如果在父组件中没有传递对应的 props 属性,则会使用默认值。
defineProps<TreeFilterProps>()
是一个函数调用,它将 props
对象的预期类型定义为 TreeFilterProps
。它返回一个所有属性均设置为 undefined
的对象。
withDefaults()
是一个函数,它接受两个参数:一个具有可选属性的对象(在这种情况下是 props
),以及一个具有默认值的对象(在这种情况下是 { id: 'id', label: 'label', multiple: false }
)。它将这两个对象合并在一起,如果有任何冲突,则优先使用默认值。
生成的 props
对象将具有与 TreeFilterProps
相同的形状,还包括额外的属性 id
、label
和 multiple
,如果未提供,则将设置为它们的默认值。
defaultProps
是另一个对象,用于设置 children
和 label
属性的默认值。它使用与 props
相同的 label
值,除非提供了不同的值。此对象的目的是为可能未由组件的调用者指定的 props 提供回退值。
ref<T>
是一个函数,它创建对类型为 T
的值的响应式引用。常量 treeRef
被赋予一个 ElTree
类型实例的引用,该类是由 Element-Plus UI 库提供给 Vue.js 的组件之一。此引用可用于访问 ElTree
实例的属性和方法。
treeData
和 treeAllData
常量也是使用 ref
创建的。它们都被赋予了一个对象数组的空数组,其中包含键值对,其中键的类型为 string
,值的类型为 any
。这些引用可用于存储将由 ElTree
组件动态呈现的数据。
在 setSelected
函数中,首先检查了 props.multiple
参数是否为 true,如果是的话则判断 props.defaultValue
是否为数组,如果是则将其赋值给 selected.value
变量,否则将其包装成一个数组并赋值给 selected.value
变量。
接着,如果 props.multiple
参数不为 true,则判断 props.defaultValue
是否为字符串类型,如果是的话直接将其赋值给 selected.value
变量,否则将 selected.value
变量赋值为空字符串。
后端项目打包部署
在控制台中执行mvn clean package
命令把项目打成一个jar包,在控制台日志中看到如下信息表明打包成功
使用XShell6 ssh客户端连接软件登录自己的Linux云服务器,执行cd /usr/local
命令进入/usr/local目录
执行mkdir logs命令创建日志文件夹
给项目启动和关闭bash脚本文件授予读、写和执行权限
chmod 775 startup.sh stop.sh
执行vim ./conf/nginx.conf
命令修改nginx.conf配置文件
$ npm install --save vue3-eventbus
import eventBus from 'vue3-eventbus'
app.use(eventBus)
// Button.vue
import bus from 'vue3-eventbus'
export default {
setup() {
bus.emit('foo', { a: 'b' })
}
}
mitt
export default class EventBus {
constructor() {
this.event = {}
}
}
Vue3提供两种组织代码逻辑的写法:
选项式(Options) API
写法<template>
<p>计数器:{{ count }}</p>
<button @click="increment">累加</button>
</template>
<script>
import { ref } fro 'vue'
export default {
setup () {
// 打印undefined
console.log(this)
// 定义数据和函数
const count = ref(0)
const increment = () => {
count.value++
}
// 返回给模板使用
return { count , increment}
}
}
</script>
setup
中通过Vue
提供的内置函数组合,实现代码功能,就是Composition API
写法。Composition API
有什么好处?可复用,可维护。setup
函数是 Vue3
特有的选项,作为组Composition API
的起点。this
不是组件实例,是 undefined
。setup
返回。Composition API
项目中几乎用不到 this
, 所有的东西通过函数获取。<template>
<p>计数器:{{ count }}</p>
<button @click="increment">累加</button>
</template>
<script setup>
import { ref } from 'vue'
// 定义数据和函数
const count = ref(0)
const increment = () => {
count.value++
}
</script>
script setup
中声明的变量都可以在模板使用,数据,函数,组件。Vue3 的 setup
中无法使用 this
这个上下文对象,但是如果我想使用 this
上的属性和方法应该怎么办呢。虽然不推荐这样使用,但依然可以通过 getCurrentInstance
方法获取上下文对象:
注意
ctx
只能在开发环境使用,生成环境为 undefined 。 推荐使用 proxy
,在开发环境和生产环境都可以使用。
<script setup>
import { getCurrentInstance } from 'vue'
// 以下两种方法都可以获取到上下文对象
const { ctx } = getCurrentInstance()
const { proxy } = getCurrentInstance()
</script>
<template>
<p>计数器:{{ count }}</p>
<button @click="increment">累加</button>
</template>
<script setup>
import { ref } from 'vue'
// 创建响应式数据对象
const count = ref(0)
const increment = () => {
// 在JS中使用的时候需.value
count.value++
}
</script>
ref
可以把简单数据或者复杂数据转换成响应式数据,.value
,.value
reactive
函数通常定义复杂类型的响应式数据,不能使用简单的数据类型。
<template>
<div>
<p>姓名:{{state.name}}</p>
<p>年龄:{{state.age}}</p>
<button @click="addAgeHandle">+1</button>
</div>
</template>
<script>
import { reactive } from "vue";
// 创建响应式数据对象
const state = reactive({ name: 'xxx', age: 25 })
const addAgeHandle = () => {
state.age++
}
</script>
<template>
<div>
<p>姓名:{{state.name}}</p>
<p>年龄:{{state.age}}</p>
<button @click="addAgeHandle1">+1</button>
<button @click="addAgeHandle2">+1</button>
</div>
</template>
<script>
import { reactive, ref } from "vue";
// 创建响应式数据对象
let user1 = reactive({ name: 'xxx', age: 25 })
let user2 = ref({ name: 'xxx', age: 25 })
const addAgeHandle = () => {
state.age++
}
// 下面是使用的对比
// 💥💥 重新赋值会导致响应式丢失
user1 = {
name:'xxx',
age: 123
}
// 👍👍 重新赋值不会导致响应式丢失
user2.value = {
name:'xxx',
age: 123
}
</script>
import { ref, reactive, isRef } from 'vue'
const count = ref(1)
const userInfo = reactive({
name:'xxx',
age: 18
})
const plusOne = computed(() => count.value + 1)
const plusUserInfo = computed(() => userInfo)
console.log(plusOne.value) // 2
// 也就是说,即使返回值写的是reactive对象,最后也会被转换为响应式的ref对象
console.log(isRef(plusUserInfo)) // true
plusOne.value++ // 错误
const count = ref(1)
const plusOne = computed({
get: () => count.value + 1,
set: (val) => {
count.value = val - 1
}
})
plusOne.value = 1
console.log(count.value) // 0
computed( () => 返回一个新的值 )
watch(基本数据,(newValue, oldValue) => { })
watch(复杂数据,(newValue, oldValue) => { }, {deep: true, immediate: true})
watch( [数据1, 数据2], (newValue, oldValue) => { })
watch( () => 复杂数据.属性名, (newValue, oldValue) => { } )
<script setup lang="ts">
import { ref, reactive, watch } from 'vue'
const count = ref(0)
const user = reactive({
name:'chengang',
age: 28
})
// 监听基本数据类型
watch(() => count, (newval, oldval) => {
console.log(newval, oldval)
})
watch(() => count.value, (newval, oldval) => {
console.log(newval, oldval)
})
watch(count, (newval, oldval) => {
console.log(newval, oldval)
})
// 监听复杂数据类型
watch(user, (newval, oldval) => {
console.log(newval, oldval)
}, { deep: true, immediate: true })
// 监听多个数据
watch([count, user], (newval, oldval) => {
console.log(newval, oldval)
}, { deep: true})
// 监听对象的属性
watch(() => user.age, (newval, oldval) => {
console.log(newval, oldval)
})
</script>
选项式API下的生命周期函数使用 | 组合式API下的生命周期函数使用 |
---|---|
beforeCreate | 不需要(直接写到setup函数中) |
created | 不需要(直接写到setup函数中) |
beforeMount | onBeforeMount |
mounted | onMounted |
beforeUpdate | onBeforeUpdate |
updated | onUpdated |
beforeDestroyed | onBeforeUnmount |
destroyed | onUnmounted |
activated | onActivated |
deactivated | onDeactivated |
const xxxRef = ref()
ref=“xxxRef”
xxxRef.value
访问dom<template>
<!-- 2. 绑定到标签ref属性上,注意:名字要和script定义的保持一致 -->
<input type="text" ref="inputRef" />
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
// 1. 调用ref函数
const inputRef = ref(null)
const focusHandle = () => {
inputRef.value.focus()
}
onMounted(() => {
focusHandle()
})
</script>
defineExpose()
暴露数据和方法,defineExpose方法无需导入即可使用<template>
<Child ref="childRef"/>
<button @click="handleClick">点击</button>
</template>
<script lang="ts" setup>
import Child from './components/child1.vue'
import { ref } from 'vue'
const childRef = ref()
const handleClick = () => {
childRef.value?.countAddHandle()
}
</script>
<template>
<Child :msg="msg"/>
</template>
<script lang="ts" setup>
import Child from './components/child1.vue'
import { ref } from 'vue'
const msg = ref('hello world')
</script>
<script lang="ts" setup>
import { computed } from 'vue'
// 使用Vue提供的方法增加对类型的声明
import type { PropType } from 'vue'
type User = {
username: string,
age: number,
address: string
}
// 第一种写法
const props = defineProps({
msg: {
type: String,
default:''
},
userList: {
type: Array as PropType<User[]>,
default: () => []
},
userInfo: {
type: Object as PropType<User>,
default: () => {}
}
})
// 第二种写法withDefaults可以赋予prop默认值,但是有一点需要注意的是,类型的声明不能使用d.ts,此为Vue3尚未解决的issur
interface ChildProps {
foo: string
userList: User[],
userInfo: User
}
const props = withDefaults(defineProps<ChildProps>(), {
foo: '1',
// 复杂数据类型的默认值需要是函数类型
userList: () => [
{ username:'admin', age: 18, address:'湖北'}
],
userInfo: () => {
return { username:'', age: 19, address:''}
}
})
</script>
使用步骤:
defineEmits
获取 emit
函数(因为没有this)emit
触发事件,并且传递数据语法:
const emit = defineEmits(["自定义事件名1", "自定义事件名2"])
emit("自定义事件名", 值)
<template>
<Child :count="count" @add-count="count++" @reduce-count="count--" />
</template>
<script lang="ts" setup>
import Child from './components/child1.vue'
import { ref } from 'vue'
const count = ref(0)
</script>
<template>
<p>{{ count }}</p>
<button @click="addHandle">+1</button>
<button @click="reduceHandle">+1</button>
</template>
<script lang="ts" setup>
// 使用defineEmits的方式1
const emits = defineEmits<{
// 向上传递参数
(e:'add-count', value?: number): void,
(e:'reduce-count'): void
}>()
// 使用defineEmits的方式二
const emits = defineEmits(['add-count', 'reduce-count'])
defineProps({
count: {
type: Number,
default:0
}
})
// 增加
const addHandle = () => {
emits('add-count')
}
// 减少
const reduceHandle = () => {
emits('reduce-count')
}
</script>
<template>
<p>{{ count }}</p>
<button @click="addHandle">+1</button>
<button @click="reduceHandle">+1</button>
</template>
<script lang="ts" setup>
// 使用defineEmits的方式1
const emits = defineEmits<{
// 向上传递参数
(e:'add-count', value?: number): void,
(e:'reduce-count'): void
}>()
// 使用defineEmits的方式二
const emits = defineEmits(['add-count', 'reduce-count'])
defineProps({
count: {
type: Number,
default:0
}
})
// 增加
const addHandle = () => {
emits('add-count')
}
// 减少
const reduceHandle = () => {
emits('reduce-count')
}
</script>
toRef()
创建的。toRef
基于响应式对象上的一个属性,创建一个对应的 ref。这样创建的 ref 与其源属性保持同步:改变源属性的值将更新 ref 的值,反之亦然。toRefs
亦然。<template>
<p>{{ name }}</p>
<p>{{ password }}</p>
<p>{{ user }}</p>
<button @click="changeHandle">修改name属性</button>
</template>
<script lang="ts" setup>
import { reactive, toRef, toRefs } from 'vue'
const user = reactive({
name: 'chengang',
password: 'admin123'
})
// 每次拿出一个
const name = toRef(user, 'name')
// 每次解构对象上全部的属性,可以只取部分
const { password } = toRefs(user)
// 这里特别需要注意:通过toRef/toRefs解构出来的新的响应式对象,值的改变会影响原来的响应式对象,换言之,他俩相互影响
const changeHandle = () => {
password.value = 'xxxxxxx'
name.value = 'this is changed'
}
</script>
如果参数是 ref,则返回内部值,否则返回参数本身。
在Vue3中,由于认为on, off,
// utils/mitt.js
import mitt from 'mitt'
import type { Emitter } from 'mitt'
type Events ={
// 事件名称:传递的参数类型
change: string
}
const emitter: Emitter<Events> = mitt()
export default emitter
// utils/mitt.ts
import mitt, { Emitter } from 'mitt'
type Events = {
// 事件名称:传递的参数类型
change: string
}
const emitter: Emitter<Events> = mitt()
export default emitter
<template>
<div class="app">
<Child1 />
<Child2 />
</div>
</template>
<script lang="ts" setup>
import Child1 from './components/child1.vue'
import Child2 from './components/child2.vue'
</script>
<template>
<button @click="changeHandle">触发修改组件2的msg</button>
</template>
<script lang="ts" setup>
import mitt from '@/utils/mitt'
const changeHandle = () => {
mitt.emit('change', '这个是组件2需要修改的内容')
}
</script>
<template>
<p>{{ msg }}</p>
</template>
<script lang="ts" setup>
import mitt from '@/utils/mitt'
import { ref } from 'vue'
const msg = ref('hello world')
mitt.on('change', (value) => {
msg.value = value
})
</script>
v-model
语法糖 完整写法
:value="count"
和 @input="count=$event.target.value"
:value="count"
和 @input="..."
v-model
语法糖 完整写法
:value="count"
和 @input="count=$event.target.value"
:modelValue="count"
和 @update:modelValue="..."
v-model
语法糖.sync
语法,一律使用v-model<input v-model="msg" type="text">
<!-- 等价 -->
<input :value="msg" @input="msg=$event.target.value" type="text">
<Demo v-model="count"></Demo>
<!-- 等价 -->
<Demo :modelValue="count" @update:modelValue="count=$event"></Demo>
<Demo v-model:count1="count1" v-model:count2="count2"></Demo>
<!-- 等价 -->
<Demo :count1="count" @update:count1="count1=$event" :count2="count" @update:count2="count2=$event"></Demo>
透传属性,所有未经props定义的属性都归纳到此处。使用useAttrs在setup语法中访问透传的属性。
<template>
<div class="app">
<Child name="name" msg="hello world" demo="123" />
</div>
</template>
<script lang="ts" setup>
import Child from './components/child.vue'
</script>
<!-- 同一个.vue文件中可以同时书写两个script,一个用来书写setup语法的代码,一个用来使用Options API的属性来增加属性 -->
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
// 关闭透传
inheritAttrs: false
})
// export default {
// inheritAttrs: false
// }
</script>
child.vue
<template>
<!-- 在模板中使用通过$attrs即可 -->
<h1 v-bind="$attrs">透传属性$attrs</h1>
</template>
<script lang="ts" setup>
import { useAttrs } from 'vue'
const attrs = useAttrs()
// 在setup中使用
console.log(attrs, '在setup中使用')
// 通过v-bind="attrs"将父祖组件传递过来的未经props定义的属性赋给元素或者组件
</script>
<template>
<div>
<slot name="header">
<h1>这里是header的默认内容</h1>
</slot>
<!-- default插槽可以写name="default",也可以不用写,默认都会归纳到default插槽 -->
<slot name="default">
</slot>
<slot name="footer">
</slot>
</div>
</template>
<template>
<div class="app">
<Child>
<!-- 具名插槽使用的时候,名称要作用到template上,除非是default -->
<template #header>
<p>header的内容</p>
</template>
<!-- #和v-slot写法都可以 -->
<template #default>
<p>默认的内容</p>
</template>
<!-- 或者default插槽直接放内容也可以 -->
<!-- <p>默认插槽的内容</p> -->
<!-- #和v-slot写法都可以 -->
<template v-slot:footer>
<p>footer的内容</p>
</template>
</Child>
</div>
</template>
<base-layout>
<template v-slot:[dynamicSlotName]>
...
</template>
<!-- 缩写为 -->
<template #[dynamicSlotName]>
...
</template>
</base-layout>
<template>
<div class="app">
<!-- 这里需要注意一下的是,Vue2的作用域插槽是通过slot-scope="scope"来取值的,但是在Vue3中,是通过v-slot -->
<!-- 如果是取具名插槽的作用域值,需要使用v-slot:插槽名称="变量名称",默认插槽是v-slot="变量名称" -->
<Child6 v-for="item in userList" :user="item" :key="item.username">
<template v-slot="{ age, username, address }">
{{ age }} - {{ username }} - {{ address }}
</template>
</Child6>
</div>
</template>
<script lang="ts" setup>
import Child6 from './components/child6.vue'
import { ref } from 'vue'
type User = {
username: string,
age: number,
address: string
}
const userList = ref<User[]>([
{ username: '用户1', age: 24, address: '地址1' },
{ username: '用户2', age: 45, address: '地址2' },
{ username: '用户3', age: 27, address: '地址3' },
])
</script>
<template>
<div class="child">
<slot :username="user.username" :age="user.age" :address="user.address"></slot>
<!-- 全部绑定所有的数据,使用下面的方法 -->
<!-- <slot v-bind="user"></slot> -->
</div>
</template>
<script lang="ts" setup>
import type { PropType } from 'vue'
type User = {
username: string,
age: number,
address: string
}
defineProps({
user: {
type: Object as PropType<User>,
default: () => {}
}
})
</script>
.sync
语法,一律使用v-model
安装 Pinia
shell
npm install pinia -S
在 main.js 中引入 Pinia
import { createApp } from 'vue';
import { createPinia } from 'pinia';
import App from './App.vue';
const app = createApp(App);
app.use(createPinia());
app.mount('#app');
使用 defineStore() 定义一个 Store 。defineStore() 第一个参数是 storeId,第二个参数是一个选项对象:
// src/stores/counter.js
import { defineStore } from 'pinia';
export const useCounterStore = defineStore('counter', {
state: () => ({ count: 0 }),
getters: {
doubleCount: (state) => state.count * 2,
},
actions: {
increment() {
this.count++;
},
},
});
defineStore() 第一个参数是 storeId ,第二个参数传入一个函数来定义 Store :
// src/stores/counter.js
import { ref, computed } from 'vue'
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
const doubleCount = computed(() => count.value * 2)
function increment() {
count.value++
}
return { count, doubleCount, increment }
})
<script setup>
import { useCounterStore } from '@/stores/counter'
const counterStore = useCounterStore()
// 以下三种方式都会被 devtools 跟踪
counterStore.count++
counterStore.$patch({ count: counterStore.count + 1 })
counterStore.increment()
</script>
<template>
<div>{{ counterStore.count }}</div>
<div>{{ counterStore.doubleCount }}</div>
</template>
prettier用来美化代码的结构,eslint用来检测代码的质量,但是二者一起使用会产生冲突,因此需要安装下面的这些依赖。
npm install prettier eslint eslint-plugin-prettier eslint-config-prettier eslint-define-config --save-dev
忽略对指定文件的代码格式化
/dist/*
.local
.output.js
/node_modules/**
**/*.svg
**/*.sh
/public/*
忽略对指定文件的代码质量的检测
*.sh
node_modules
*.md
*.woff
*.ttf
.vscode
.idea
dist
/public
/docs
.husky
.local
/bin
Dockerfile
.eslintrc.js
文件// @ts-check
const { defineConfig } = require('eslint-define-config');
module.exports = defineConfig({
root: true,
env: {
browser: true,
node: true,
es6: true,
},
parser: 'vue-eslint-parser',
parserOptions: {
parser: '@typescript-eslint/parser',
ecmaVersion: 2020,
sourceType: 'module',
jsxPragma: 'React',
ecmaFeatures: {
jsx: true,
},
},
extends: [
"eslint:recommended",
"plugin:vue/vue3-essential",
"plugin:@typescript-eslint/recommended",
"prettier",
],
rules: {
'vue/script-setup-uses-vars': 'error',
'@typescript-eslint/ban-ts-ignore': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-var-requires': 'off',
'@typescript-eslint/no-empty-function': 'off',
'vue/custom-event-name-casing': 'off',
'no-use-before-define': 'off',
'@typescript-eslint/no-use-before-define': 'off',
'@typescript-eslint/ban-ts-comment': 'off',
'@typescript-eslint/ban-types': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-unused-vars': [
'error',
{
argsIgnorePattern: '^_',
varsIgnorePattern: '^_',
},
],
'no-unused-vars': [
'error',
{
argsIgnorePattern: '^_',
varsIgnorePattern: '^_',
},
],
'space-before-function-paren': 'off',
'vue/attributes-order': 'off',
'vue/v-on-event-hyphenation': 'off',
'vue/multi-word-component-names': 'off',
'vue/one-component-per-file': 'off',
'vue/html-closing-bracket-newline': 'off',
'vue/max-attributes-per-line': 'off',
'vue/multiline-html-element-content-newline': 'off',
'vue/singleline-html-element-content-newline': 'off',
'vue/attribute-hyphenation': 'off',
'vue/require-default-prop': 'off',
'vue/html-self-closing': [
'error',
{
html: {
void: 'always',
normal: 'never',
component: 'always',
},
svg: 'always',
math: 'always',
},
],
},
});
特别需要注意的是,由于vite设置了"type": "module"
后你的所有js文件默认使用ESM模块规范,不支持commonjs规范,所以必须显式的声明成xxx.cjs才能标识这个是用commonjs规范的,因此需要把你的配置文件都改成.cjs后缀,例如prettier的配置文件。
// prettier.config.js
module.exports = {
// 一行最多多少个字符
printWidth: 120,
// 指定每个缩进级别的空格数
tabWidth: 2,
// 使用制表符而不是空格缩进行
useTabs: true,
// 在语句末尾是否需要分号
semi: false,
// 是否使用单引号
singleQuote: true,
// 更改引用对象属性的时间 可选值"<as-needed|consistent|preserve>"
quoteProps: 'as-needed',
// 在JSX中使用单引号而不是双引号
jsxSingleQuote: false,
// 多行时尽可能打印尾随逗号。(例如,单行数组永远不会出现逗号结尾。) 可选值"<none|es5|all>",默认none
trailingComma: 'none',
// 在对象文字中的括号之间打印空格
bracketSpacing: true,
// jsx 标签的反尖括号需要换行
jsxBracketSameLine: false,
// 在单独的箭头函数参数周围包括括号 always:(x) => x \ avoid:x => x
arrowParens: 'always',
// 这两个选项可用于格式化以给定字符偏移量(分别包括和不包括)开始和结束的代码
rangeStart: 0,
rangeEnd: Infinity,
// 指定要使用的解析器,不需要写文件开头的 @prettier
requirePragma: false,
// 不需要自动在文件开头插入 @prettier
insertPragma: false,
// 使用默认的折行标准 always\never\preserve
proseWrap: 'preserve',
// 指定HTML文件的全局空格敏感度 css\strict\ignore
htmlWhitespaceSensitivity: 'css',
// Vue文件脚本和样式标签缩进
vueIndentScriptAndStyle: false,
//在 windows 操作系统中换行符通常是回车 (CR) 加换行分隔符 (LF),也就是回车换行(CRLF),
//然而在 Linux 和 Unix 中只使用简单的换行分隔符 (LF)。
//对应的控制字符为 "\n" (LF) 和 "\r\n"(CRLF)。auto意为保持现有的行尾
// 换行符使用 lf 结尾是 可选值"<auto|lf|crlf|cr>"
endOfLine: 'auto'
}
在package.json中配置一个scripts:
"prettier": "prettier --write ."
执行下面的命令,将当前所有的文件按照prettier进行格式化
npm run prettier
Mac系统中使用Command + Shift + P
打开
版本较低的浏览器不支持ES6的语法和新API,而Babel默认只转换新的JavaScript句法,不转换新的API,比如Proxy、Symbol、Promise等全局对象,以及一些定义在全局对象上的方法都不会转码。
// vite.config.js
import legacy from '@vitejs/plugin-legacy'
export default {
plugins: [
legacy({
// 需要兼容的目标列表,可以设置多个
targets: ['defaults', 'ie >= 11', 'chrome >= 52'],
additionalLegacyPolyfills: ['regenerator-runtime/runtime'],
renderLegacyChunks:true,
// 下面的数组可以自定义添加低版本转换的方法
polyfills:[
'es.symbol',
'es.array.filter',
'es.promise',
'es.promise.finally',
'es/map',
'es/set',
'es.array.for-each',
'es.object.define-properties',
'es.object.define-property',
'es.object.get-own-property-descriptor',
'es.object.get-own-property-descriptors',
'es.object.keys',
'es.object.to-string',
'web.dom-collections.for-each',
'esnext.global-this',
'esnext.string.match-all'
]
})
]
}
仓库地址:https://github.com/webVueBlog/WebGuideInterview
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有