API
1、createApp
- 创建vue实例
import { createApp } from 'vue'
const app = createApp({
/* 根组件选项 */
})
//可以传入根组件选项和app
import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)
2、createSSRApp
- 以SSR激活模式创建vue实例
- 用法与createAPP相同
3、app.mount
参数可以是一个实际的 DOM 元素或一个 CSS 选择器 (使用第一个匹配到的元素)。返回根组件的实例。
如果该组件有模板或定义了渲染函数,它将替换容器内所有现存的 DOM 节点。否则在运行时编译器可用的情况下,容器元素的 innerHTML 将被用作模板。
在 SSR 激活模式下,它将激活容器内现有的 DOM 节点。如果出现了激活不匹配,那么现有的 DOM 节点将会被修改以匹配客户端的实际渲染结果。
对于每个应用实例,mount() 仅能调用一次。
4、app.component()
1.注册全局组件方法一:
app.component('Comp',Comp)传入组件名和组件实例,Comp是SFC
<script setup>
import { ref,createApp } from 'vue'
import Comp from './Comp.vue'
const msg = ref('Hello World!')
const app = createApp({})
app.component('Comp',Comp)
</script>
<template>
<h1>{{ msg }}</h1>
<Comp/>
<input v-model="msg" />
</template>
- 注册全局组件方法二
传入配置项
{template:'',data:function (){return {}}}
Vue.component("my-component",
{ template:"<div @click='count++'>{{count}}</div>",
data: function(){ return {count:0} }
})
1、声明式渲染
1.1、单文件组件
- Vue 单文件组件 (Single-File Component,缩写为 SFC)。
- 将从属于同一个组件的 HTML、CSS 和 JavaScript 封装在使用
.vue后缀的文件中。
1.2、响应式对象
-
reactive()API 来声明响应式状态。 - 将初始对象作为参数传入
- 由
reactive()创建的对象都是 JavaScript Proxy,其行为与普通对象一样:
import { reactive } from 'vue'
const counter = reactive({
count: 0
})
console.log(counter.count) // 0
counter.count++
- ref可以包装任何类型的值,参数是初始值
- 返回一个对象,有value属性
import { ref } from 'vue'
const message = ref('Hello World!')
console.log(message.value) // "Hello World!"
message.value = 'Changed'
把数据包装成响应式数据之后,修改数据,页面会同步展示
<script setup>
import { ref } from 'vue'
let message = ref('make me dynamic')
message = '556'
// 组件逻辑
// 此处声明一些响应式状态
</script>
<template>
<h1>{{message}}</h1>
</template>
2、属性绑定
语法::属性名 = “对象名”
<div :id="dynamicId"></div>
2.1 class绑定语法
不要忘记:和“”
- 值为对象:键名为类名,键值为是否显示类名
<div v-bind:class="{ active: isActive, 'text-danger': hasError }"></div>
- 数组语法-每一个数组名就是一个类
<div v-bind:class="[activeClass, errorClass]"></div>
3、事件监听
语法-@click = “事件处理函数名”注意别加括号
4、表单绑定
将input的value值和text值绑定,不用手写oninput函数。
<input v-model="text">
5、if else
<h1 v-if="awesome">Vue is awesome!</h1>
<h1 v-else>Oh no 😢</h1>
6、列表渲染
<ul>
<li v-for="todo in todos" :key="todo.id">
{{ todo.text }}
</li>
</ul>
v-for="todo in todos"
应用实例-添加列表项-删除列表项
<script setup>
import { ref } from 'vue'
// 给每个 todo 对象一个唯一的 id
let id = 0
const newTodo = ref('')
const todos = ref([
{ id: id++, text: 'Learn HTML' },
{ id: id++, text: 'Learn JavaScript' },
{ id: id++, text: 'Learn Vue' }
])
function addTodo() {
todos.value.push({ id: id++, text: newTodo.value })
newTodo.value = ''
}
function removeTodo(todo) {
todos.value = todos.value.filter((t) => t !== todo)
}
</script>
<template>
<form @submit.prevent="addTodo">
<input v-model="newTodo" required placeholder="new todo">
<button>Add Todo</button>
</form>
<ul>
<li v-for="todo in todos" :key="todo.id">
{{ todo.text }}
<button @click="removeTodo(todo)">X</button>
</li>
</ul>
</template>
- 上面是一个单文件组件例子
- 在script里面定义变量和函数,在template里面定义视图模版
<input id = 'todo.id' type = "checkbox" v-model="todo.done" name = "course">{{todo.text}}
computed计算属性
- 些数据是根据现有的响应式数据计算得出时,就可以使用计算属性(computed properties)。
- 计算属性会基于它们依赖的响应式数据进行计算,并且会缓存计算结果。
- 只有在响应式数据变化时才会重新计算。
- 参数是箭头函数,返回新的值
const completedTodos = computed(() => { return todos.value.filter(todo => todo.done) })
watch侦听器
watch(todoId,()=>{
todoData.value.id = todoId
})
- 有两个参数,第一个参数是被侦听的响应式数据
- 第二个参数是回调函数,处理副作用
- 一旦被侦听的数据改变,就会执行回调函数
props
- 父组件给子组件传递动态参数,要用:v-bind语法
<ChildComp :msg = "greeting" />
//如果不用:语法会显示greeting
- 子组件引入props
- vue3提出的defineprops可以在编译时优化
const props = defineProps({ title: String, count: { type: Number, default: 0 } })
@input="$emit('update:modelValue', $event.target.value)"解析
@input:这是 Vue 中用于监听输入事件的一种缩写语法。它等价于v-on:input,表示监听<input>元素的输入事件。$emit('update:modelValue', $event.target.value):这是在输入事件触发时执行的表达式。$emit是 Vue 实例的方法,用于触发自定义事件。在这里- 它触发了一个名为
update:modelValue的自定义事件,并传递了一个参数$event.target.value - 其中
$event是事件对象,$event.target.value表示输入框当前的值。
总的来说,这段代码的作用是:当 <input> 元素的值发生变化时,触发一个名为 update:modelValue 的自定义事件,并将输入框的当前值作为参数传递给父组件。这种模式通常用于实现 Vue 中的双向绑定。
插槽
除了通过 props 传递数据外,父组件还可以通过插槽 (slots) 将模板片段传递给子组件:
<ChildComp>
This is some slot content!
</ChildComp>
传入的值会在子组件的slot处展示
<template>
<slot></slot>
</template>
1、$emit
<!-- ChildComponent.vue -->
<template>
<button @click="emitEvent">Click me!</button>
</template>
<script>
export default {
methods: {
emitEvent() {
// 触发名为 'custom-event' 的自定义事件,并传递一些数据
this.$emit('custom-event', 'Hello from ChildComponent!');
}
}
}
</script>
- 父组件定义函数
methods: { handleCustomEvent(data) { // 当 'custom-event' 事件被触发时,执行这个方法 this.message = data; } - 父组件传递函数给子组件
<ChildComponent @custom-event="handleCustomEvent" /> - 子组件使用
this.$emit调用父组件的函数this.$emit('custom-event', 'Hello from ChildComponent!');
<!-- ParentComponent.vue -->
<template>
<div>
<p>{{ message }}</p>
<!-- 使用 ChildComponent 组件,并监听 'custom-event' 事件 -->
<ChildComponent @custom-event="handleCustomEvent" />
</div>
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
components: {
ChildComponent
},
data() {
return {
message: ''
};
},
methods: {
handleCustomEvent(data) {
// 当 'custom-event' 事件被触发时,执行这个方法
this.message = data;
}
}
}
</script>
2、使用$ref
<!-- ParentComponent.vue -->
<template>
<div>
<button @click="incrementChildCounter">Increment Child Counter</button>
<!-- 使用 ref 特性引用 ChildComponent -->
<ChildComponent ref="childComponentRef" />
</div>
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
components: {
ChildComponent
},
methods: {
incrementChildCounter() {
// 通过 this.$refs 访问引用的子组件,并调用子组件的方法
this.$refs.childComponentRef.increment();
}
}
}
</script>
- 子组件添加ref属性
<ChildComponent ref="childComponentRef" /> - 父组件获取子组件对象:
this.$refs.childComponentRef.increment();
3、使用
<!-- ParentComponent.vue -->
<template>
<div>
<!-- 使用 ChildComponent 组件 -->
<ChildComponent />
</div>
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
components: {
ChildComponent
},
provide() {
// 提供一个名为 message 的数据
return {
message: 'Hello from ParentComponent!'
};
}
}
</script>
<!-- ChildComponent.vue -->
<template>
<div>
<p>{{ providedMessage }}</p>
</div>
</template>
<script>
export default {
inject: ['message'], // 注入名为 message 的数据
computed: {
providedMessage() {
// 使用注入的 message 数据
return this.message;
}
}
}
</script>
-
父组件
provide() { // 提供一个名为 message 的数据 return { message: 'Hello from ParentComponent!' }; }一个对象有属性 -
子组件
inject: ['message'], // 注入名为 message 的数据然后使用this.message
3、事件总线
首先,我们需要创建一个事件总线实例。我们可以在单独的文件中创建它,例如 EventBus.js:
// EventBus.js
import Vue from 'vue';
export const EventBus = new Vue();
然后在 SiblingA 组件中,当按钮被点击时,我们触发一个自定义事件,并通过事件总线发送消息
EventBus.$emit('message', 'Hello from SiblingA!');
<!-- SiblingA.vue -->
<template>
<button @click="sendMessage">Send Message</button>
</template>
<script>
import { EventBus } from './EventBus';
export default {
methods: {
sendMessage() {
// 触发名为 'message' 的自定义事件,并传递消息
EventBus.$emit('message', 'Hello from SiblingA!');
}
}
}
</script>
接下来,在 SiblingB 组件中,我们监听 message 事件,并在事件触发时接收并显示收到的消息:
EventBus.$on('message', message => {});
<!-- SiblingB.vue -->
<template>
<div>
<p>Received Message: {{ receivedMessage }}</p>
</div>
</template>
<script>
import { EventBus } from './EventBus';
export default {
data() {
return {
receivedMessage: ''
};
},
mounted() {
// 监听名为 'message' 的自定义事件
EventBus.$on('message', message => {
// 收到消息后更新 receivedMessage 数据
this.receivedMessage = message;
});
}
}
</script>
1、setup
<script setup>是在单文件组件 (SFC) 中使用组合式 API 的编译时语法糖。当同时使用 SFC 与组合式 API 时该语法是默认推荐。相比于普通的<script>语法,它具有更多优势:
- 更少的样板内容,更简洁的代码。
- 能够使用纯 TypeScript 声明 props 和自定义事件。
- 更好的运行时性能 (其模板会被编译成同一作用域内的渲染函数,避免了渲染上下文代理对象)。
- 更好的 IDE 类型推导性能 (减少了语言服务器从代码中抽取类型的工作)。
- 更好的类型推断:由于
setup()函数中的代码是在编译时执行的,而不是在运行时执行的,因此 TypeScript 能够更好地推断出组件的类型信息,从而提供更好的类型检查和错误提示。 - 更好的性能优化:
setup()函数中的代码执行时机是在组件实例创建之前 - 与普通的
<script>只在组件被首次引入的时候执行一次不同,<script setup>中的代码会在每次组件实例被创建的时候执行。
1.1 、顶层的绑定会被暴露给模板
<script setup>
// 变量
const msg = 'Hello!'
import { capitalize } from './helpers'
// 函数
function log() {
console.log(msg)
}
</script>
<template>
<button @click="log">{{ msg }}</button>
<div>{{ capitalize('hello') }}</div>
</template>
- 可以在模板表达式中直接使用导入的 helper 函数,而不需要通过
methods选项来暴露它。
2、动态组件
<component :is="currentComponent"></component>- 根据:is 的值判断显示哪个组件
<template>
<div>
<button @click="currentComponent = 'Home'">Home</button>
<button @click="currentComponent = 'About'">About</button>
<component :is="currentComponent"></component>
</div>
</template>
<script>
import Home from './components/Home.vue';
import About from './components/About.vue';
export default {
components: {
Home,
About
},
data() {
return {
currentComponent: 'Home'
};
}
};
</script>
3、命名空间组件
<script setup>
import * as Form from './form-components'
</script>
<template>
<Form.Input>
<Form.Label>label</Form.Label>
</Form.Input>
</template>
举例
src/
|-- components/
| |-- Form/
| | |-- Input.vue
| | |-- Select.vue
| |-- UI/
| |-- Button.vue
| |-- Modal.vue
<template>
<div>
<Form.Input />
<UI.Button />
</div>
</template>
<script setup>
import Form from '@/components/Form/Input.vue';
import UI from '@/components/UI/Button.vue';
</script>
4、自定义指令
<template>
<div>
<input v-my-upper-directive />
<p v-my-upper-directive>This text will be converted to uppercase</p>
</div>
</template>
<script setup>
const vMyUpperDirective= {
mounted(el) {
el.addEventListener('input', () => {
el.value = el.value.toUpperCase();
});
}
};
</script>
- 在 Vue 自定义指令中,
el是指令绑定的目标元素,即指令所作用的 DOM 元素。在mounted钩子函数中,el参数表示指令所绑定的 DOM 元素。 - 上面代码中
el表示使用自定义指令的元素,例如<input v-uppercase />和<p v-uppercase>中的<input>和<p>元素。在这个例子中,el将分别指向这两个元素。
获取dom
哪一个生命周期都可以,只要在异步方法中
beforeCreate
拿不到method中的方法
在created中还是mounted中请求
根据情况,如果希望子组件的先展示则在mounted中请求
keep-alive
- 新增生命周期
activited deactivited - 缓存组件
<keep-alive>组件</keep-alive>- 首次进入组件执行5个生命周期
- 后续进入组件执行1个
activited
生命周期使用情况
this.$parent
可以直接修改父组件的数据
provide/inject
- 只能向下传递
provide(){retturn { value: 'value'}}inject: ['value']
子组件传值给父组件
this.$childrenthis.$emit('','')
ref/refs
this.refs.child
ref = 'child
eventBus
import bus from './util/bus.js'
bus.$emit('change',this.str)
子组件
bus.$on('change',(str)=>{})
this.$root
返回根组件
slot
父组件
<List>
<template #header>
<div>hello</div>
</template>
</List>
子组件
<slot name = 'header'>
作用域插槽
子组件
<slot :air = 'air'></slot>
父组件
<template #header = '{air}'></template>
vuex
用法
//vuex文件
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use('Vuex')
export default new Vuex.Store({
state:{ str : '123'},
getters:{},
mutations:{},
actions:{},
modules:{}
})
//其他组件
this.$store.state.str
mapState
import mapState from 'vuex'
computed :{...mapState(['str'])}
mapState是一个函数,接受的参数可以是对象也可以是数组,将Stroe仓库的数据复制了一份。
getters
vuex文件
getters:{
changeStr(state){
return state.str = '123'
}
使用组件
$store.getters.changeStr
or
import mapGetters from 'vuex'
computed:{
...mapGetters(['changeStr'])
}
}
两种都无法改变原来的数据,只能获取
mutation和action
1.mutation只能执行同步操作
2. action是提交mutation的,可以写异步操作
3. 只能通过mutation修改state
mutation:{
changeNumber(state){
state.count++
}
}
this.$store.commit('changeNumber')
action:{
changeNumberAsync({commit}){
setTimeOut(()=>{
commit('changeNumber')
},1000)
}
}
this.$store.dispatch('changeNumberAsync')
vuex实现持久化存储
- 自己写localStorage
- 使用插件
vuex-persistedstate
import 'createPersistedState' from 'vuex-persistedstate'
Vue.use('vuex')
const store = new Vuex.store({
plugin:[
createPersistedState({
storage:window.sessionStorage,
key:'store'
})
]
})
路由
安装
- 下载cdn
- 使用包管理器
npm install vue-router@4 - 使用create-vue脚手架自动加入vue-router选项
import {createRouter} from 'vue-router'
router-link
router-view
app.use(router)
app.use(router)//使整个应用支持router
//router对象是路由对象,由createRouter创建
const router = VueRouter.createRouter({
history:VueRouter.createWebhasHistory(),
routes,
})
//routes是路由配置,是路径和组件的对应关系
//是对象数组,对象有path和component属性
const routes = [{path:'/', component:Home},{path:'/about', component:About}]
常见模式
- hash #
- history /
找不到页面
- history会给后端发送请求
- hash模式不会
打包的差异
- 配置
publicPath:''./,然后运行npm run build
const router = new VueRouter({
mode:'history/hash' //如果选择history模式,自测时看不到内容,hash模式可以看到
})
当前页跳转当前页故障
- 在routerPush里面加上
catch(error => error)
route
- router是整个路由对象
- route是当前路由对象
导航守卫
- 全局前置守卫
router.beforeEach((from,to,next)=>{
next()
})
- 全局后置守卫 没有next函数,无法改变路由
router.afterEach((from,to)=>{
})
- 路由独享守卫 在进入一个页面之前判断是否登陆,如果没有登陆则跳转登陆页面
const router = new VueRouter({
routes:[
{
path:'',
beforeEnter:(from,to,next)=>{}
}
]
})
- 组件内的守卫
$set
this.$set(obj,'key',newValue)
$nextTick()
在create函数里面异步获取dom
原理是promise
this.nextTick(()=>{
})
原理
nextTick(callback){
return Promise.resolve.then(()=>callback())
}
获取根组件,数据,子节点,父节点
$el,$data, $children $parent $root
data定义数据在return内部和外部的区别
- 定义在return之外的数据无法定义为响应式,也就是没有get和set拦截
computded计算属性
1. 能不能直接修改
computed:{
changeStr(){
get(){
return this.str.slice(-2)
}
set(val){
this.str = val
}
}
}
可以用作v-model吗
可以,但是一定要使用get和set写法
watch用法
watch:{
str(newValue,oldValue){
execute when str changed
}
}
1. how to run watch when initiate program
immedate:true
watch: {
str:{
handle(oldvalue,newvalue){
}
immediate:true
}
}
deep watch
deep:true
the difference between method and computed attribute
- computed attribute has cache. only run once for the same value
directive
whole directive
- use
Vue.directive('name',()=>{actiion :function()})
//main.js
Vue.directive('demo',()=>{
inserted:function(){
}
})