难过的打火机 · VUE自定义(有限)库存日历插件 - 刘家三胖 · 6 天前 · |
刚失恋的黄瓜 · Vue项目中使用JS监听浏览器关闭、刷新、后退事件· 昨天 · |
绅士的熊猫 · Vue项目中使用JS监听浏览器关闭、刷新、后 ...· 昨天 · |
听话的皮蛋 · vue浏览器关闭触发的事件是什么 • ...· 昨天 · |
不要命的饼干 · 江苏省人民政府 2017年第十八期 ...· 1 月前 · |
傻傻的开水瓶 · 新闻事件 - Exegenesis Bio ...· 3 月前 · |
高兴的自行车 · 驻旧金山总领事罗林泉夫人乔力参加硅谷女性“母 ...· 4 月前 · |
干练的罐头 · 微信视频号运营 风控篇 完全指导手册 | ...· 5 月前 · |
讲道义的甘蔗 · Thurston Moore’s ...· 5 月前 · |
Vue 是一套用于构建用户界面的渐进式框架
注意:Vue 3.x 版本对应 Vue Router 4.x 路由版本
已安装
16.0
或更高版本的 Node.js
$ npm init vue@latest
指令将会安装并执行 create-vue,它是 Vue 官方的项目脚手架工具
✔ Project name: … <your-project-name>
✔ Add TypeScript? … No/Yes
✔ Add JSX Support? … No/Yes
✔ Add Vue Router for Single Page Application development? … No/Yes
✔ Add Pinia for state management? … No/Yes
✔ Add Vitest for Unit testing? … No/Yes
✔ Add Cypress for both Unit and End-to-End testing? … No/Yes
✔ Add ESLint for code quality? … No/Yes
✔ Add Prettier for code formatting? … No/Yes
Scaffolding project in ./<your-project-name>...
Done.
安装依赖并启动开发服务器
$ cd <your-project-name>
$ npm install
$ npm run dev
当你准备将应用发布到生产环境时,请运行:
$ npm run build
此命令会在 ./dist
文件夹中为你的应用创建一个生产环境的构建版本
import { createApp, ref } from 'vue'
const app = createApp({
setup() {
const message = ref("Hello Vue3")
return {
message
app.mount('#app')
<div id="app">
<button @click="count++">
{{ count }}
</button>
</div>
使用 ES 模块构建版本
<div id="app">{{ message, ref }}</div>
<script type="module">
import { createApp, ref } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js'
createApp({
setup() {
const message = ref("Hello Vue3")
return {
message
}).mount('#app')
</script>
<span>Message: {{ msg }}</span>
使用的是 Mustache
语法 (即双大括号),每次 msg
属性更改时它也会同步更新
动态绑定多个值
通过不带参数的
v-bind
,你可以将它们绑定到单个元素上
<script setup>
import comp from "./Comp.vue"
import {ref} from "vue"
const a = ref("hello")
const b = ref("world")
</script>
<template>
<comp v-bind="{a, b}"></comp>
</template>
如果你是使用的 setup
语法糖。需要使用 defineprops
声名(可以直接使用a
/b
)
const props = defineProps({
a: String,
b: String
响应式基础
<div>{{ state.count }}</div>
import { defineComponent, reactive } from 'vue';
// `defineComponent`用于IDE推导类型
export default defineComponent({
// setup 用于组合式 API 的特殊钩子函数
setup() {
const state = reactive({ count: 0 });
// 暴露 state 到模板
return {
state
});
<button @click="increment">
{{ state.count }}
</button>
import { defineComponent, reactive } from 'vue';
export default defineComponent({
setup() {
const state = reactive({ count: 0 });
function increment() {
state.count++;
// 不要忘记同时暴露 increment 函数
return {
state,
increment
用
ref()
定义响应式变量
reactive
只能用于对象、数组和
Map
、
Set
这样的集合类型,对 string、number 和 boolean 这样的原始类型则需要使用
ref
import { ref } from 'vue';
const count = ref(0);
console.log(count); // { value: 0 }
console.log(count.value); // 0
count.value++;
console.log(count.value); // 1
const objectRef = ref({ count: 0 });
// 这是响应式的替换
objectRef.value = { count: 1 };
const obj = {
foo: ref(1),
bar: ref(2)
// 该函数接收一个 ref
// 需要通过 .value 取值
// 但它会保持响应性
callSomeFunction(obj.foo);
// 仍然是响应式的
const { foo, bar } = obj;
在 html 模板中不需要带 .value
就可以使用
<script setup>
import { ref } from 'vue';
const count = ref(0);
</script>
<template>
{{ count }}
</div>
</template>
有状态方法
import { reactive, defineComponent, onUnmounted } from 'vue';
import { debounce } from 'lodash-es';
export default defineComponent({
setup() {
// 每个实例都有了自己的预置防抖的处理函数
const debouncedClick = debounce(click, 500);
function click() {
// ... 对点击的响应 ...
// 最好是在组件卸载时
// 清除掉防抖计时器
onUnmounted(() => {
debouncedClick.cancel();
});
});
响应式进阶 —— watch 和 computed
<script setup>
import { ref, watch } from 'vue';
const count = ref(0)
const isEvent = ref(false)
function increment() {
state.count++
watch(count, function() {
isEvent.value = count.value % 2 === 0
</script>
<template>
<button @click="increment">
{{ count }}
</button>
is event: {{ isEvent ? 'yes' : 'no' }}
</template>
监听多个值
<template>
<h1> {{ count1 }} </h1>
<h1> {{ count2 }} </h1>
<button @click="count1++">count1</button>
<button @click="count2++">count2</button>
</template>
<script setup>
import { watch, ref } from 'vue';
const count1 = ref(0)
const count2 = ref(0)
watch(
// 监听的表达式或函数
() => ({
count1: count1.value,
count2: count2.value
}),
// 回调函数
(newValue, oldValue) => {
// 在这里执行需要的逻辑
console.log('count1 或 count2 变化了:', newValue);
// immediate: true 表示在初始渲染时立即执行一次回调函数,以便处理初始的状态。
// deep: true 表示深度监听,即对 newValue 和 oldValue 进行深层比较,而不是简单的引用比较。
{ immediate: true, deep: true }
</script>
<style scoped>
</style>
<script setup>
import { ref, computed } from 'vue';
const text = ref('')
// computed 的回调函数里
// 会根据已有并用到的状态计算出新的状态
const capital = computed(function(){
return text.value.toUpperCase();
</script>
<template>
<input v-model="text" />
<p>to capital: {{ capital }}</p>
</template>
defineProps
<script setup>
import { defineProps } from 'vue';
// 这里可以将 `username` 解构出来,
// 但是一旦解构出来再使用,就不具备响应式能力
defineProps({
username: String
</script>
<template>
<p>username: {{ username }}</p>
</template>
子组件定义需要的参数
<script setup>
const username = 'vue'
</script>
<template>
<children :username="username" />
</template>
父组件参入参数
defineEmits
<script setup>
import { defineEmits, ref } from 'vue';
const emit = defineEmits(['search'])
const keyword = ref('')
const onSearch = function() {
emit('search', keyword.value)
</script>
<template>
<input v-model="keyword" />
<button @click="onSearch">search</button>
</template>
子组件定义支持 emit
的函数
<script setup>
const onSearch = function(keyword){
console.log(keyword)
</script>
<template>
<children @search="onSearch" />
</template>
父组件绑定子组件定义的事件
defineExpose
<script setup>
import { defineExpose, ref } from 'vue';
const keyword = ref('')
const onSearch = function() {
console.log(keyword.value)
defineExpose({ onSearch })
</script>
<template>
<input v-model="keyword" />
</template>
子组件对父组件暴露方法
<script setup>
import { ref } from 'vue'
const childrenRef = ref(null)
const onSearch = function() {
childrenRef.value.onSearch()
</script>
<template>
<children ref='childrenRef' />
<button @click="onSearch">search</button>
</template>
父组件调用子组件的方法
Provide / Inject
import type { InjectionKey, Ref } from 'vue'
export const ProvideKey = Symbol() as InjectionKey<Ref<string>>
在应用中使用 ProvideKey
<script setup lang="ts">
import { provide, ref } from 'vue'
import { ProvideKey } from './types'
const text = ref<string>('123')
provide(ProvideKey, text)
</script>
<template>
<input v-model="text" />
</template>
父组件为后代组件提供数据
<script setup lang="ts">
import { inject } from 'vue'
import { ProvideKey } from './types'
const value = inject(ProvideKey)
</script>
<template>
<h4>{{value}}</h4>
</template>
后代组件注入父组件提供的数据
1. 路由的基本使用
开启命名空间后,组件中读取state数据
方式一:自己直接读取
this.$store.state.personAbout.list
方式二:借助 mapState 读取:
...mapState('countAbout',[
'sum','school','subject'
]),
开启命名空间后,组件中读取getters数据
方式一:自己直接读取
this.$store.getters[
'personAbout/firstPersonName'
方式二:借助 mapGetters 读取:
...mapGetters('countAbout',['bigSum'])
开启命名空间后,组件中调用dispatch
方式一:自己直接 dispatch
this.$store.dispatch(
'personAbout/addPersonWang', person
方式二:借助mapActions:
...mapActions('countAbout',{
incrementOdd:'jia0dd',
incrementWait:'jiaWait'
开启命名空间后,组件中调用commit
方式一:自己直接 commit
this.$store.commit(
'personAbout/ADD_PERSON', person
方式二:借助 mapMutations:
...mapMutations('countAbout', {
increment:'JIA',decrement:'JIAN'
}),
2. 路由的使用
import VueRouter from 'vue-router'
// 引入Luyou 组件
import About from '../components/About'
import Home from '../components/Home'
// 创建router实例对象,去管理一组一组的路由规则
const router = new VueRouter({
routes: [
path: '/about',
component: About
path: '/home',
component: Home
// 暴露 router
export default router
实现切换(active-class可配置高亮样式)
<router-link
active-class="active"
to="/about">
About
</router-link>
指定展示位置
<router-diew></router-view>
几个注意点
路由组件通常存放在pages文件夹,一般组件通常存放在components文件夹。
通过切换,“隐藏”了的路由组件,默认是被销毁掉的,需要的时候再去挂载。
每个组件都有自己的$route属性,里面存储着自己的路由信息。
整个应用只有一个router,可以通过组件的srouter 属性获取到。
3.路由的query
<template>
<ul class="list">
<!-- to的对象写法 -->
<li v-for="item of data" :key="item.id">
<router-link
class="link"
:to="{
path:'/home/message/mes',
query: { id:item.id, title:item.mes }
>{{item.mes}}</router-link>
<router-view></router-view>
</div>
</template>
<script>
export default {
name:'HomeChild1',
data() {
return {
data:[
{id:1,mes:"消息1"},
{id:2,mes:"消息2"},
{id:3,mes:"消息3"}
</script>
<style scoped>
.list { margin-left:80px; }
.link{
color: orange;
text-decoration: none;
background-color: skyblue;
</style>
接收参数 {{$route.query.id}}
跳转路由并携带参数
<li v-for="item of data" :key="item.id">
<router-link
class="link"
:to="`/home/message/mes?id=${item.id}&title=${item.mes}`"
{{item.mes}}
</router-link>
4. 命名路由
routes:[
{ path:'/about', component:AboutBody },
path:'/home',
component:HomeBody,
children:[
{ path:'news', component:HomeChild },
path:'message',
component:HomeChild1,
//多级路由
children:[
{ name:'richu', path:'mes', component:HomeMessage }
<router-link :to="{
name:'',
path:'/home/message/mes',
query:{ id:item.id,title:item.mes }
5.params参数的使用
1. 声明接收
children:[
name:'richu',
path:'mes/:id/:title',
component:HomeMessage
2. 传递
<li v-for="item of data" :key="item.id">
<router-link
class="link"
:to="`/home/message/mes/${item.id}/${item.mes}`"
>{{item.mes}}
</router-link>
3. 接收
<li>编号{{$route.params.id}}</li>
<li>标题{{$route.params.title}}</li>
6.props的使用
路由的props配置
name
:
'xiangqing'
,
path
:
'detail/:id'
,
component
:
Detail
作用:让路由组件更方便的收到参数
//第一种写法:props值为对象,该对象中所有的key-value的组合最终都会通过props传给Detai1组件
// props:{a:900]
//第二种写法:props值为布尔值,布尔值为true,则把路由收到的所有params参数通过props传给Detai1组件
// props:true
//第三种写法:props值为函数,该函数返回的对象中每一组key-value都会通过props传给Detail组件
props(route){
return {
id:route.query.id,
title:route.query.title
<router-link> 的 replace 属性
作用:控制路由跳转时操作浏览器历史记录的模式
浏览器的历史记录有两种写入方式:分别为 push和replace,默认为push
如何开启replace 模式: push
是追加历史记录,replace
是替换当前记录[路由跳转时候 <router-link replace>News\</router-link>
]
为组件的 props 标注类型
当使用
<script setup>
时,
defineProps()
宏函数支持从它的参数中推导类型
<script setup lang="ts">
const props = defineProps({
foo: { type: String, required: true },
bar: Number
props.foo // string
props.bar // number | undefined
</script>
对同一个文件中的一个接口或对象类型字面量的引用:
interface Props {/* ... */}
defineProps<Props>()
Props 解构默认值
export interface Props {
msg?: string
labels?: string[]
const props = withDefaults(defineProps<Props>(), {
msg: 'hello',
labels: () => ['one', 'two']
使用目前为实验性的响应性语法糖
<script setup lang="ts">
interface Props {
name: string
count?: number
// 对 defineProps() 的响应性解构
// 默认值会被编译为等价的运行时选项
const {
name, count = 100
} = defineProps<Props>()
</script>
为事件处理函数标注类型
<script setup lang="ts">
function handleChange(event) {
// `event` 隐式地标注为 `any` 类型
console.log(event.target.value)
</script>
<template>
<input
type="text"
@change="handleChange" />
</template>
显式地为事件处理函数的参数标注类型
function handleChange(event: Event) {
const target = event.target as HTMLInputElement
console.log(target.value)
为组件模板引用标注类型
<!-- MyModal.vue -->
<script setup lang="ts">
import { ref } from 'vue'
const isContentShown = ref(false)
const open =
() => (isContentShown.value = true)
defineExpose({
</script>
使用 TypeScript 内置的 InstanceType
工具类型来获取其实例类
<!-- App.vue -->
<script setup lang="ts">
import MyModal from './MyModal.vue'
type Modal = InstanceType<typeof MyModal>
const modal = ref<Modal | null>(null)
const openModal = () => {
modal.value?.open()
</script>
选项式 API 为组件的 props 标注类型
import { defineComponent } from 'vue'
export default defineComponent({
// 启用了类型推导
props: {
name: String,
id: [Number, String],
msg: { type: String, required: true },
metadata: null
mounted() {
// 类型:string | undefined
this.name
// 类型:number|string|undefined
this.id
// 类型:string
this.msg
// 类型:any
this.metadata
使用 PropType 这个工具类型来标记更复杂的 props 类型
import { defineComponent } from 'vue'
import type { PropType } from 'vue'
interface Book {
title: string
author: string
year: number
export default defineComponent({
props: {
book: {
// 提供相对 `Object` 更确定的类型
type: Object as PropType<Book>,
required: true
// 也可以标记函数
callback: Function as PropType<(id: number) => void>
mounted() {
this.book.title // string
this.book.year // number
// TS Error: argument of type 'string' is not
// assignable to parameter of type 'number'
this.callback?.('123')
选项式 API 为组件的 emits 标注类型
import { defineComponent } from 'vue'
type Payload = { bookName: string }
export default defineComponent({
emits: {
addBook(payload: Payload) {
// 执行运行时校验
return payload.bookName.length > 0
methods: {
onSubmit() {
this.$emit('addBook', {
bookName: 123 // 类型错误
// 类型错误
this.$emit('non-declared-event')
选项式 API 为计算属性标记类型
计算属性会自动根据其返回值来推导其类型:
import { defineComponent } from 'vue'
export default defineComponent({
data() {
return {
message: 'Hello!'
computed: {
greeting() {
return this.message + '!'
mounted() {
this.greeting // 类型:string
在某些场景中,你可能想要显式地标记出计算属性的类型以确保其实现是正确的:
import { defineComponent } from 'vue'
export default defineComponent({
data() {
return {
message: 'Hello!'
computed: {
// 显式标注返回类型
greeting(): string {
return this.message + '!'
// 标注一个可写的计算属性
greetingUppercased: {
get(): string {
return this.greeting.toUpperCase()
set(newValue: string) {
this.message = newValue.toUpperCase()
选项式 API 扩展全局属性
import axios from 'axios'
declare module 'vue' {
interface ComponentCustomProperties {
$http: typeof axios
$translate: (key: string) => string
类型扩展的位置
我们可以将这些类型扩展放在一个 .ts
文件,或是一个影响整个项目的 *.d.ts
文件中
// 不工作,将覆盖原始类型。
declare module 'vue' {
interface ComponentCustomProperties {
$translate: (key: string) => string
// 正常工作。
export {}
declare module 'vue' {
interface ComponentCustomProperties {
$translate: (key: string) => string
选项式 API 扩展自定义选项
某些插件,比如 vue-router,提供了一些自定义的组件选项,比如 beforeRouteEnter:
import { defineComponent } from 'vue'
export default defineComponent({
beforeRouteEnter(to, from, next) {
// ...
如果没有确切的类型标注,这个钩子函数的参数会隐式地标注为 any
类型。我们可以为 ComponentCustomOptions
接口扩展自定义的选项来支持:
import { Route } from 'vue-router'
declare module 'vue' {
interface ComponentCustomOptions {
beforeRouteEnter?(
to: Route,
from: Route,
next: () => void
): void
全局 API - 应用实例
:-
:-
createApp()
创建一个应用实例
#
createSSRApp()
以
SSR 激活
模式创建一个应用实例
#
app.mount()
将应用实例挂载在一个容器元素中
#
app.unmount()
卸载一个已挂载的应用实例
#
app.provide()
提供一个可以在应用中的所有后代组件中注入使用的值
#
app.component()
注册或获取全局组件
#
app.directive()
注册或获取全局指令
#
app.use()
安装一个插件
#
app.mixin()
全局注册一个混入
#
app.version
当前应用所使用的 Vue 版本号
#
app.config
获得应用实例的配置设定
#
app.config.errorHandler
为应用内抛出的未捕获错误指定一个全局处理函数
#
app.config.warnHandler
为 Vue 的运行时警告指定一个自定义处理函数
#
app.config.performance
在浏览器开发工具中追踪性能表现
#
app.config.compilerOptions
配置运行时编译器的选项
#
app.config.globalProperties
注册全局属性对象
#
app.config.optionMergeStrategies
定义自定义组件选项的合并策略的对象
#
组合式 API - setup()
:-
:-
onMounted()
组件挂载完成后执行
#
onUpdated()
状态变更而更新其 DOM 树之后调用
#
onUnmounted()
组件实例被卸载之后调用
#
onBeforeMount()
组件被挂载之前被调用
#
onBeforeUpdate()
状态变更而更新其 DOM 树之前调用
#
onBeforeUnmount()
组件实例被卸载之前调用
#
onErrorCaptured()
捕获了后代组件传递的错误时调用
#
onRenderTracked()
组件渲染过程中追踪到响应式依赖时调用
#
onRenderTriggered()
响应式依赖的变更触发了组件渲染时调用
#
onActivated()
若组件实例是 <KeepAlive> 缓存树的一部分,当组件被插入到 DOM 中时调用
#
onDeactivated()
若组件实例是 <KeepAlive> 缓存树的一部分,当组件从 DOM 中被移除时调用
#
onServerPrefetch()
组件实例在服务器上被渲染之前调用
#
选项式 API - 生命周期选项
:-
:-
beforeCreate
组件实例初始化完成之后立即调用
#
created
组件实例处理完所有与状态相关的选项后调用
#
beforeMount
组件被挂载之前调用
#
mounted
组件被挂载之后调用
#
beforeUpdate
状态变更而更新其 DOM 树之前调用
#
updated
状态变更而更新其 DOM 树之后调用
#
beforeUnmount
组件实例被卸载之前调用
#
unmounted
组件实例被卸载之后调用
#
errorCaptured
捕获了后代组件传递的错误时调用
#
renderTracked
Dev only
组件渲染过程中追踪到响应式依赖时调用
#
renderTriggered
Dev only
响应式依赖的变更触发了组件渲染时调用
#
activated
若组件实例是
缓存树的一部分,当组件被插入到 DOM 中时调用
#
deactivated
若组件实例是
缓存树的一部分,当组件从 DOM 中被移除时调用
#
serverPrefetch
SSR only
组件实例在服务器上被渲染之前调用
#
内置内容 - 指令
:-
:-
v-text
更新元素的
textContent
#
v-html
更新元素的
innerHTML
#
v-show
切换元素的
display
css 属性
#
v-if
有条件地渲染元素
#
v-else
#
v-else-if
#
v-for
多次渲染元素或模板块
#
v-on
绑定事件监听器
#
v-bind
动态地绑定一个或多个属性
#
v-model
创建双向绑定
#
v-slot
提供插槽或接收 props 的插槽
#
v-pre
跳过元素和它的子元素编译过程
#
v-once
只渲染元素和组件一次
#
v-memo
(3.2+)
缓存一个模板的子树
#
v-cloak
保持在元素上直到实例结束编译
#
serverPrefetch
SSR only
组件实例在服务器上被渲染之前调用
#
难过的打火机 · VUE自定义(有限)库存日历插件 - 刘家三胖 6 天前 |