Web开发-Vue3.0上手 如果你忘了 看这一pian就够了!

2024-02-04 18:40

本文主要是介绍Web开发-Vue3.0上手 如果你忘了 看这一pian就够了!,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Web开发-Vue3.0上手 如果你忘了 看这一pian就够了!

之前用Vue3.0开发过一个项目雏形,后续需求尚未跟进,有些忘了,so,在继续开发之前,没事看看Vue3的知识,简单回顾,强化技能;

Study from 2021 Vue3.0极速上手

vue3基础知识参考Web开发-Vue3.x

主要内容

相比Vue2的主要变化:

  • Fragments和Emits
  • 自定义渲染器
  • 全局API调整
  • 摇树优化
  • v-model调整
  • 更好用的渲染函数
  • 失宠的函数式组件
  • 异步组件的使用
  • data选项、组件白名单
  • transition类名调整
  • watch API重要调整
  • $on $off $once被删除了
  • filter为何也被移除
  • 。。。

TodoList实践;

vue-router 4.x

  • 新用法 新特性
  • history选项
  • 通配处理
  • isReady方法
  • 滚动行为变化
  • keep-alive和transition与router-view使用的重要变化
  • router-link使用调整
  • 动态路由解析变化
  • composition API

vuex 4.x

  • 新用法
  • composition API

如何快速开始Vue3.0

  • cdn方式
  • vue-cli(> v4.5):
    • npm i -g @vue/cli@next
    • 创建项目时,会有相应的vue3选项;
  • vite:更快捷
    • npm init vite-app <project-name>
    • cd <project-name>
    • npm install
    • npm run dev

使用vite开发时,模块间引入文件的后缀名不可省略;

相比Vue2的主要变化:

composition API

更好的逻辑复用和代码组织


<p> {{ counter.counter }} </p>
<p> {{ counter.doubleCounter }} </p>import {reactive, computed, onMountd, onUnmounted} from 'vue'
exprot default {name:'',props:{msg:String},setup() {// counter 相关操作逻辑// 响应式对象const data = useCounter()// or 不提取//  const data = reactive({//    counter: 1,//   //  计算属性//    doubleCounter: computed(()=> data.counter * 2)//  })// let timer// //  声明周期监听回调// onMountd(()=>{//   // 定义定时器//   timer = setInterval(()=>{//     data.counter++//   }, 1000)// })// onUnmounted(()=>{//   // 取消定时器//   clearInterval(timer)// })// 其他逻辑const msg2 = ref('something')return {data, msg2}}
}// 提取:counter 相关操作逻辑
function useCounter() {const data = reactive({counter: 1,//  计算属性doubleCounter: computed(()=> data.counter * 2)})let timer//  声明周期监听回调onMountd(()=>{// 定义定时器timer = setInterval(()=>{data.counter++}, 1000)})onUnmounted(()=>{// 取消定时器clearInterval(timer)})return data
}
// 对象解包
const {counter, doubleCounter} = useCounter() function useCounter() {...// 可以把响应式对象中的各个key 转换为单值响应式的ref数据return toRefs(data)
}
// ref引用 DOM元素
<p ref='desc'></p>...setup() {// ref响应式数据const {counter, doubleCounter} = useCounter()// reactive响应式数据const data = useCounter()  ...// 使用元素引用const desc = ref(null)// ref监听器watch(counter, (val, oldVal) => {// 访问ref单值响应式数据 需要使用.value属性const pElement = desc.value;pElement.textContent = `value change ${val} ${oldValue}`})// reactive监听器watch(() => data.counter, (val, oldVal) => {// 访问ref单值响应式数据 需要使用.value属性const pElement = desc.value;pElement.textContent = `value change ${val} ${oldValue}`})return {data, counter, doubleCounter, desc}
}
Teleport

传送门组件:提供了一种简洁的方式可以指定它里面内容的父元素

<template><button @click="modalOpen = true">弹出一个全屏模态窗口</button><!-- 传送组件是一个全局组件 包含的就是要传送的内容 to指定的是一个选择器--><teleport to="body"><div v-if="modalOpen" class="modal"><div>这是一个模态窗口我的父元素是body<button @click="modalOpen=false">Close</button></div></div></teleport>
</template>
<script>
export default {setup(){let modalOpen = ref(true)}
}
</script>
<style scoped>
.modal {position: absolute;top:0; right:0; bottom:0; left:0;background-color:rgba(0,0,0,0.5);display:flex;flex-direction: column;align-items: center;justify-content:center;
}.modal div {display: flex;flex-direction: column;align-items: center;justify-content:center;background-color:white;width: 300px;height: 300px;padding: 5px;
}</style>
Fragments

Vue3中组件可以拥有多个根;

<template><header>..<main>..<footer>..
</template>
Emits Component Option

vue3中组件发送的自定义事件需要定义在emits选项中:

// 定义组件 Emit
<Emit><template><div>// 如果与原生事件名重名 那么该事件会被触发两次 通过在emits选项中进行配置 可以使触发只有一次<p @click="$emit('click')">Click</p>// 自定义事件会被触发 这可以更好的指示组件工作<p @click="$emit('myClick')">MyClick</p></div></template>export default {// emits选项emits: ['click','myClick']}
</Emit>// 使用组件 Emit
<Emit @click='onClick'></Emit>
<Emit @myClick='onClick'></Emit>
自定义渲染器 custom renderer

这个API可以用来自定义渲染逻辑;如自定义一个画布的渲染过程;

全局API调整

Global API改为应用程序实例调用

  • Vue2没有app概念,new Vue()得到的根实例被作为app;这样的根实例是共享相同的全局配置,这在测试时会污染其他测试用例;Vue.xxx
  • 全局配置也导致没有办法在单页面创建不同全局配置的多个app实例;
  • vue3中使用createApp返回app实例,由它暴露一系列全局api;app.xxx

主要受影响的API:

  • Vue.config <=> app.config
  • Vue.component <=> app.component
  • Vue.directive <=> app.directive
  • Vue.mixin <=> app.mixin
  • Vue.use <=> app.use
摇树优化

vue2中不少global-api是作为静态函数直接挂在在构造函数上,如Vue.nextTick(),如果代码中未用到,会形成dead code,这类代码无法使用webpack的tree-shaking排除掉;

vue3中则将他们抽取为独立函数,这样打包工具的摇树优化可以将这些dead code排除掉;

import {nextTick} from 'vue'nextTick(() => {// 
})
v-model调整

model选项 和 v-bind的sync修饰符被移除,统一为v-model参数形式;

// 示例:在子组件中修改传入的父组件值
// 父组件部分
<SubComponent v-model:counter='counter'></SubComponent>
// 等价于下面这个,这也是我们在vue2中使用的方式 显然现在更简洁了
// <SubComponent :counter='counter' @update:counter="counter=$event"></SubComponent>// 子组件部分
<template><div @click="$emit('update:counter', counter + 1)"> Counter: {{counter}}</div>
</template>export default {props: {// 父组件如果不指定字段名 默认展开的字段名为 modelValuecounter: {type: Number,default:0 },},
}
更好用的渲染函数API
  • 不再传入h函数,需要手动导入;
  • 拍平 props结构;
  • 作用域插槽scopedSlots删掉了,统一到slots;

<RenderTest><template>默认插槽</template><template v-slot:content>具名插槽</template>
</RenderTest>import {h} from 'vue'componets:{RenderTest: {render(){...}}
}
// ...的具体实现
render() {const emit = this.$emitconst modelValue = this.modelValueconst onclick = this.onclick// 获取插槽内容this.$slots.default()this.$slots.content()retrun h('div', [h('div', {onClick() {emit('update:modelValue', modelValue + 1)},`I am comp, ${modelValue}`}),h('button', {onClick() {onclick()},'buty it!'})])
}
失宠的函数式组件

函数式组件仅能通过简单函数方式创建,functional选项废弃;

异步组件的使用

异步组件要求使用defineAsyncComponent方法创建;

  • 必须使用defineAsyncComponent包裹
  • component选项重命名为loader
  • Loader函数不在接收resolveandreject且必须返回一个Promise;
import {defineAsyncComponent} from 'vue'// 不带配置的异步组件
const asyncPage = defineAsyncComponent(()=> import('./NextPage.vue'))// 带配置的异步组件(这里loader选项就是以前的component选项)
import ErrorComponent from './components/ErrorComponent.vue'
import LoadingComponent from './components/LoadingComponent.vue'const asyncPageWithOptions = defineAsyncComponent({loader: () => import('./NextPage.vue'),delay: 200,timeout: 3000,errorComponent: ErrorComponent,loadingComponent: LoadingComponent
})
data选项

组件data选项应该总是声明为函数,返回响应式数据;

组件白名单

vue3中 自定义元素检测发生在模板编译时,如果要添加一些vue之外的自定义元素,避免编译时的检测,需要在编译器选项中设置isCustomElement选项;

使用构建工具时,模板都会用vue-loader预编译,设置它提供的compilerOptions即可:

// vue.config.js
rules: [{test: /\.vue$/,use: 'vue-loader',options: {compilerOptions: {// 标签为plastic-button时 放过,可以设置多个isCustomElement: tag => tag === 'plastic-button'}}}// ...
]

使用vite演示的话,在vite.config.js中配置 vueCompilerOptions即可:

module.exports = {vueCompilerOptions: {isCunstomElement: tag => tag === 'piechart'}
}

如果采用的是运行时编译的vue,可以通过全局配置isCunstomElement:

const app = Vue.createApp({})
app.config.sCunstomElement = tag => tag === 'piechart'
is属性仅限于用在component标签上

vue3设置动态组件,使用is可以确定实际渲染的组件是哪一个;但在vue3中,is属性仅限于用在component标签上;

dom内模板解析可以使用v-is指令替代;

<table>// 实际渲染的是blog-post-row组件<tr v-is="'blog-post-row'" :data='xxx'></tr>
</table>component('blog-post-row', {props: ['data'],template: "<tr><td>{{this.data}}</td></tr>"
})
自定义指令API 和 组件保持一致
bind -> beforeMount
inserted -> mounted
beforeUpdate
update -> [removed]
componnetUpdated -> updated
beforeUnmount
unbind -> unmounted
createApp(App).directive('highlight', {beforeMount(el, binding, vnode) {el.style.background = binding.value}
})// 使用
<p v-highlight></p>
transition类名调整
  • v-enter -> v-enter-from
  • v-leave -> v-leave-from
v-enter-from
v-enter-activev-enter-tov-leave-from
v-leave-activev-leave-to<transition name='fade'><p v-if="show"> hellow </p>
</transition><style scoped>
// 淡入淡出
.fade-enter-active,
.fade-leave-active {transition: opacity 0.5s ease;
}.fage-enter-from,
.fade-leave-to {opacity: 0;
}<style>
watch API重要调整

组件watch选项和实例方法$watch不再支持点分隔符字符串路径

可以使用计算函数作为$watch的参数实现对带路径的支持;

this.$watch(()=> this.foo.bar, (v1,v2) => {})
keyCode 作为v-on修饰符被移除

keyCode 作为v-on修饰符被移除,只支持别名方式

// keycode不支持
<input v-on:keyup.13="submit" />
// alias支持
<input v-on:keyup.enter="submit" />
$on $off $once被删除了

这三个被认为是不应该由vue提供的,可以使用其他第三方库实现;

<script src="https://unpkg.com/mitt/dist/mitt.umd.js"></script>// 创建emitter(放在公共的地方 并且导出)
export const emitter = mitt()
// import {emitter} from ...// 发送事件
emitter.emit('foo', 'foooooo')// 监听事件
emitter.on('foo', msg => console.log(msg))

filter也被移除了;

TodoList实践

MVC实现

功能预览:

在这里插入图片描述

代码实现(Vue3):

<!-- Todos.vue -->
<template><div><inputtype="text"v-model="newTodo"@keyup.enter="addTodo"autofocusplaceholder="新增今日待办"autocomplete="off"/><ul><liv-for="todo in filterdTodos":key="todo.id":class="{completed: todo.completed, editing: todo === editedTodo}"><div class="view"><input type="checkbox" v-model="todo.completed"><label @dblclick="editTodo(todo)">{{todo.title}}</label><button @click="removeTodo(todo)">X</button></div><!-- 双击:编辑待办 --><!-- 我们希望双击时 input可以自动获取焦点 这样体验更好 这里应用到自定义指令 --><inputtype="text"class="edit"v-model="todo.title"v-todo-focus="todo=== editedTodo"@blur="doneEdit(todo)"@keyup.enter="doneEdit(todo)"@keyup.escape="cancelEdit(todo)"/></li></ul><!-- 过滤功能:计算属性 + watch的应用 --><p class="filters"><span @click="visibility = 'all'" :class="{selected: visibility === 'all'}">All</span><span @click="visibility = 'active'" :class="{selected: visibility === 'active'}">Active</span><span @click="visibility = 'completed'" :class="{selected: visibility === 'completed'}">Completed</span></p></div>
</template><script>
import { reactive, toRefs, computed, watchEffect } from 'vue'const filters = {all (todos) {console.log('tag', 'all')return todos},active (todos) {console.log('tag', 'active')return todos.filter(todo => !todo.completed)},completed (todos) {console.log('tag', 'completed')return todos.filter(todo => todo.completed)}
}// 缓存操作:刷新数据不丢失
const todoStorage = {fetch () {const todos = JSON.parse(localStorage.getItem('vue3-todos') || '[]')todos.forEach((todo, index) => {todo.id = index + 1})return todos},save (todos) {localStorage.setItem('vue3-todos', JSON.stringify(todos))}
}export default {setup () {const state = reactive({newTodo: '',beforeEditCache: '', // 缓存编辑前的titleeditedTodo: null,todos: todoStorage.fetch(),visibility: 'all',filterdTodos: computed(() => {// 计算属性return filters[state.visibility](state.todos)})})function addTodo () {state.todos.push({id: state.todos.length + 1,title: state.newTodo,completed: false})state.newTodo = ''}function editTodo (todo) {state.beforeEditCache = todo.titlestate.editedTodo = todo}function removeTodo (todo) {state.todos.splice(state.todos.indexOf(todo), 1)}function doneEdit (todo) {// 数据的变更 已经 由双向绑定完成 这里只需要把editTodo置空即可state.editedTodo = null}function cancelEdit (todo) {todo.title = state.beforeEditCachestate.editedTodo = null}// 监听副作用watchEffect(() => {// 只要todos改变 就缓存todoStorage.save(state.todos)})return {...toRefs(state),addTodo,editTodo,removeTodo,doneEdit,cancelEdit}},directives: {'todo-focus': (el, { value }) => {if (value) {el.focus()}}}}
</script><style scoped>.completed label{/* 删除装饰线 */text-decoration: line-through;
}.edit,
.editing .view {display: none;
}.view,
.editing .edit {display: block;
}.filters > span {cursor: pointer;padding: 5px 10px;border: 1px solid transparent;
}
.filters > span.selected {border: 1px solid black;border-radius: 5px;
}</style>
组件化实践

针对上边的Todos进行组件化优化重构;

先来看看利用组件化和 Composition API 优化后的Todos.vue

<template><div><EditTodov-model:todo-title="newTodo"@keyup.enter="addTodo"autofocusplaceholder="新增今日待办"autocomplete="off"></EditTodo><ul><TodoItemv-for="todo in filterdTodos":key="todo.id":todoC="todo"v-model:edited-todo="editedTodo"@remove-todo="removeTodo"></TodoItem></ul><!-- 过滤功能:计算属性 + watch的应用 --><Filter:items="filterItems"v-model="visibility"></Filter></div>
</template><script>
import { reactive, toRefs } from 'vue'
import TodoItem from './TodoItem.vue'
import Filter from './Filter.vue'
import { useTodos } from './useTodos.js'
import { useFilter } from './useFilter.js'export default {components: {TodoItem,Filter},setup () {const todoState = reactive({newTodo: '',editedTodo: null})const { todos, addTodo, removeTodo } = useTodos(todoState)const filterState = useFilter(todos)return {...toRefs(todoState),...toRefs(filterState),addTodo,removeTodo}}
}
</script><style scoped></style>

具体的变化:

  • 组件部分:
    • input标签进行了组件封装,参考EditTodo.vue
    • 值得注意的是EditTodo组件是在main.js中被注册为全局组件,参考main.js
    • li标签及其内容进行了组件提取,参考TodoItem.vue
    • 过滤的三个功能按钮进行了组件化封装,参考Filter.vue
  • 组合API调整:
    • Todos相关操作(添加、移除、缓存)的逻辑状态 提取到了useTodos.js中;
    • Fliter相关状态(响应式) 提取到了useFilter.js中;
    • 之后将组合之后的状态、操作 return出去;
EditTodo.vue

$attrs

<template><inputtype="text":value="todoTitle"@input="onInputChange"v-bind="$attrs"/>
</template><script>
export default {props: {todoTitle: {type: String,default: ''}},methods: {onInputChange (e) {this.$emit('update:todoTitle', e.target.value)}}}
</script><style></style>
main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'import EditTodo from './components/todos/EditTodo.vue'createApp(App).component('EditTodo', EditTodo) // 全局注册组件(局部注册与之前一致).directive('hightlight', { // 注册全局指令beforeMount (el, binding, vnode) {el.style.background = binding.value}}).use(store).use(router).mount('#app')
TodoItem.vue

emit配置项、自定义指令

<template><li:class="{completed: todo.completed, editing: todo === editedTodo}"><div class="view"><input type="checkbox" v-model="todo.completed"><label @dblclick="editTodo(todo)">{{todo.title}}</label><button @click="removeTodo(todo)">X</button></div><!-- 双击:编辑待办 --><!-- 我们希望双击时 input可以自动获取焦点 这样体验更好 这里应用到自定义指令 --><EditTodoclass="edit"v-model:todo-title="todo.title"v-todo-focus="todo=== editedTodo"@blur="doneEdit(todo)"@keyup.enter="doneEdit(todo)"@keyup.escape="cancelEdit(todo)"></EditTodo></li>
</template><script>
import { reactive, toRefs } from 'vue'export default {props: {todoC: {type: Object,required: true},editedTodo: Object},// emit配置项emits: ['remove-todo', 'update:edited-todo'],setup (props, { emit }) {const state = reactive({todo: props.todoC, // 避免v-model绑定修改单向数据流造成的异常beforeEditCache: '' // 缓存编辑前的title})function editTodo (todo) {state.beforeEditCache = todo.titleemit('update:edited-todo', todo)}function removeTodo (todo) {emit('remove-todo', todo)}function doneEdit (todo) {// 数据的变更 已经 由双向绑定完成 这里只需要把editTodo置空即可emit('update:edited-todo', null)}function cancelEdit (todo) {todo.title = state.beforeEditCacheemit('update:edited-todo', null)}return {...toRefs(state),editTodo,removeTodo,doneEdit,cancelEdit}},directives: {'todo-focus': (el, { value }) => {if (value) {el.focus()}}}
}
</script><style scoped>.completed label{/* 删除装饰线 */text-decoration: line-through;
}.edit,
.editing .view {display: none;
}.view,
.editing .edit {display: block;
}
</style>
Filter.vue
<template><p class="filters"><spanv-for="item in items":key='item.value'@click="$emit('update:modelValue', item.value)":class="{selected: modelValue === item.value}">{{item.label}}</span></p>
</template><script>
export default {props: ['items', 'modelValue'],emits: ['update:modelValue']
}
</script><style scoped>
.filters > span {cursor: pointer;padding: 5px 10px;border: 1px solid transparent;
}
.filters > span.selected {border: 1px solid black;border-radius: 5px;
}
</style>
useTodos.js

watchEffect


import { ref, watchEffect } from 'vue'// 缓存操作:刷新数据不丢失
const todoStorage = {fetch () {const todos = JSON.parse(localStorage.getItem('vue3-todos') || '[]')todos.forEach((todo, index) => {todo.id = index + 1})return todos},save (todos) {localStorage.setItem('vue3-todos', JSON.stringify(todos))}
}export function useTodos (state) {const todos = ref(todoStorage.fetch())function addTodo () {todos.value.push({id: todos.value.length + 1,title: state.newTodo,completed: false})state.newTodo = ''}function removeTodo (todo) {todos.value.splice(todos.value.indexOf(todo), 1)}// 监听副作用watchEffect(() => {// 只要todos改变 就缓存todoStorage.save(todos.value)})return {todos,addTodo,removeTodo}
}
useFilter.js
import { reactive, computed } from 'vue'const filters = {all (todos) {console.log('tag', 'all')return todos},active (todos) {console.log('tag', 'active')return todos.filter(todo => !todo.completed)},completed (todos) {console.log('tag', 'completed')return todos.filter(todo => todo.completed)}
}export function useFilter (todos) {const filterState = reactive({filterItems: [{ label: 'All', value: 'all' },{ label: 'Active', value: 'active' },{ label: 'Completed', value: 'completed' }],visibility: 'all',filterdTodos: computed(() => {// 计算属性return filters[filterState.visibility](todos.value)})})return filterState
}

vue-router 4.x

  • <router-view>
  • <router-link to="/">
// 路由实例注册:
createApp(App).use(router).mount('#app')// 路由实例创建
import {createRouter, createWebHistory} from 'vue-router'
const routes = [{path: '/user',name: 'user-layout',component: () => import('@/layout/user-layout/user-layout'),redirect: '/user/login',//如果将第一个child的path设置为空串 这里可以不使用redirect 重定向children: [{path: '/user/login',name: 'login',component: () => import('@/views/user/login')}]},{path: 'path: "/:pathMatch(.*)*"',name: '404',// 每个路由中都可以携带一个元数据对象 用于携带信息meta: {auth: false,title: 'notFound'},component: () =>import( /* webpackChunkName: "fail" */ '@/views/error/notFound')}
];const router = createRouter({history: createWebHistory(process.env.BASE_URL),routes
})// 路由动态添加
router.addRoute({path:'/about',name:'about',component: () => import('./components/About.vue')
})router.addRoute('about',{path: './about/info',component: {render() {return h('div', 'info page')}}
})// 结合组合API的使用
import {useRouter, useRoute} from "vue-router"
import {watch} from "vue"setup(){const router = useRouter();const route = useRoute();// 监听routewatch(()=>route.params,(newparams)=>{})watch(()=>route.query,(newquery)=>{})// 守卫onBeforeRouteLeave((to,from) => {const answer = window.confirm("Sure Leave?")if (!answer){return false}})onBeforeRouteUpdate((to,from) => {})return {}
}
<!-- // 新接口 useLink的使用(如包装 router-link标签称为更高级的组件)
// 创建 NavLink.vue -->
<template><div :class="{active: isActive}" @click="navigate">{{route.name}}</div>
</template><script>
import {RouterLink, useLink} from 'vue-router'export default {props: {...RouterLink.props,inactiveClass: String},setup(props) {const {route, href, isActive, isExactActive, navigate} = useLink(props)return {route, isActive, navigate}}
}
</script><style scoped>
.active {background-color: #3fb983;
}
</style>

history选项 代替了mode选项

  • history => createWebHistory('/base-prefix') 可以指定公共的路由前缀
  • hash => createWebHashHistory()
  • abstract => createMemoryHistory() 用于服务端渲染

base选项移至 createWebHistory方法中;

通配符(*)被移除了,如而代之的是:

  • path: '*' => path: "/:pathMatch(.*)*"
// 使用命名导航至404页面
router.resolve({name:'404',params: {pathMatch: ['not', 'found']}
})
  • isReady() 替代了 onReady()
router.push()
// before
router.onReady(onSuccess, onError)
// new
router.isReady().then(onSuccess).catch(onError)
  • scrollBehavior行为发生了变化
const router = createRouter({history:...,routes:[],scrollBehavior(to, from, savedPosition){// {x:10, y:10} before// {left:10, top:10} newif (savedPosition) {// 记录并设置 页面的滚动位置return savedPosition}ekse {return {top: 10}}}
})
  • keep-alivetransition 必须用在 router-view内部
    • 以前是在外边的
<router-view v-slot="{Component}"><keep-alive><component :is="Component" /></keep-alive>
</router-view>
  • router-link移除了一些属性
    • append属性
    • tag/event
    • exact:完全比配逻辑简化了 仅支持完全相同
// before append
<router-link to="child-route" append>
// byself
<router-link :to="append($route.path, 'child-route')">app.config.globalPropertier.append = (path, pathToAppend) => {return path + pathToAppend
}// before tag
<router-link to="/xx" tag="span" event="dblclick"></router-link>
// new 
<router-link to="/xx" custom v-slot="{navigate}"><span @dblclick="navigate">xxx</span>
</router-link>
  • mixins中的路由守卫将被忽略;
  • match方法被移除了,使用resolve替代;
    • resolve返回的是一个location,多数路由跳转还是用push,push返回的是一个promise;
  • 移除router.getMatchedComponents(),用于服务端渲染的;
    • 可以用router.currentRoute.value.matched这种方式访问匹配的路由;
  • 包括首屏导航在内所有导航均为异步;
    • 如果首屏存在路由守卫,则可以不等待就绪就直接挂载
// 如果需要首屏前动画
app.use(router)
router.isReady().then(()=> app.mount('#app'))
  • route的parent属性被移除
    • 替换:const parent = this.$route.matched[this.$route.matched.length - 2]
createRouter({strict: boolean,sensitive: boolean
})
  • router创建时 routes选项是必填项;
  • 跳转不存在的命名路由 会报错;
  • 必填参数缺失会抛异常;
  • 命名子路由 如果path为空 将不再追加/
    • 这可能会给设置了重定向redirect选项的子路由带来副作用;

vuex 4.x

vuex4是vuex3的兼容版本,提供和vuex3相同的API;

// 创建store实例
import {createStore} from 'vuex'const store = createStore({state() {return {count: 1}},mutations: {add(state){state.count++;}}
})app.use(store)// composition API
import {useStore} from 'vuex'const store = useStore()return  {...toRefs(store.state),add() {store.commit('add')}
}

这篇关于Web开发-Vue3.0上手 如果你忘了 看这一pian就够了!的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/678469

相关文章

Vue3 的 shallowRef 和 shallowReactive:优化性能

大家对 Vue3 的 ref 和 reactive 都很熟悉,那么对 shallowRef 和 shallowReactive 是否了解呢? 在编程和数据结构中,“shallow”(浅层)通常指对数据结构的最外层进行操作,而不递归地处理其内部或嵌套的数据。这种处理方式关注的是数据结构的第一层属性或元素,而忽略更深层次的嵌套内容。 1. 浅层与深层的对比 1.1 浅层(Shallow) 定义

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

【 html+css 绚丽Loading 】000046 三才归元阵

前言:哈喽,大家好,今天给大家分享html+css 绚丽Loading!并提供具体代码帮助大家深入理解,彻底掌握!创作不易,如果能帮助到大家或者给大家一些灵感和启发,欢迎收藏+关注哦 💕 目录 📚一、效果📚二、信息💡1.简介:💡2.外观描述:💡3.使用方式:💡4.战斗方式:💡5.提升:💡6.传说: 📚三、源代码,上代码,可以直接复制使用🎥效果🗂️目录✍️

Hadoop企业开发案例调优场景

需求 (1)需求:从1G数据中,统计每个单词出现次数。服务器3台,每台配置4G内存,4核CPU,4线程。 (2)需求分析: 1G / 128m = 8个MapTask;1个ReduceTask;1个mrAppMaster 平均每个节点运行10个 / 3台 ≈ 3个任务(4    3    3) HDFS参数调优 (1)修改:hadoop-env.sh export HDFS_NAMENOD

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

Andrej Karpathy最新采访:认知核心模型10亿参数就够了,AI会打破教育不公的僵局

夕小瑶科技说 原创  作者 | 海野 AI圈子的红人,AI大神Andrej Karpathy,曾是OpenAI联合创始人之一,特斯拉AI总监。上一次的动态是官宣创办一家名为 Eureka Labs 的人工智能+教育公司 ,宣布将长期致力于AI原生教育。 近日,Andrej Karpathy接受了No Priors(投资博客)的采访,与硅谷知名投资人 Sara Guo 和 Elad G

嵌入式QT开发:构建高效智能的嵌入式系统

摘要: 本文深入探讨了嵌入式 QT 相关的各个方面。从 QT 框架的基础架构和核心概念出发,详细阐述了其在嵌入式环境中的优势与特点。文中分析了嵌入式 QT 的开发环境搭建过程,包括交叉编译工具链的配置等关键步骤。进一步探讨了嵌入式 QT 的界面设计与开发,涵盖了从基本控件的使用到复杂界面布局的构建。同时也深入研究了信号与槽机制在嵌入式系统中的应用,以及嵌入式 QT 与硬件设备的交互,包括输入输出设

OpenHarmony鸿蒙开发( Beta5.0)无感配网详解

1、简介 无感配网是指在设备联网过程中无需输入热点相关账号信息,即可快速实现设备配网,是一种兼顾高效性、可靠性和安全性的配网方式。 2、配网原理 2.1 通信原理 手机和智能设备之间的信息传递,利用特有的NAN协议实现。利用手机和智能设备之间的WiFi 感知订阅、发布能力,实现了数字管家应用和设备之间的发现。在完成设备间的认证和响应后,即可发送相关配网数据。同时还支持与常规Sof

活用c4d官方开发文档查询代码

当你问AI助手比如豆包,如何用python禁止掉xpresso标签时候,它会提示到 这时候要用到两个东西。https://developers.maxon.net/论坛搜索和开发文档 比如这里我就在官方找到正确的id描述 然后我就把参数标签换过来

Java Web指的是什么

Java Web指的是使用Java技术进行Web开发的一种方式。Java在Web开发领域有着广泛的应用,主要通过Java EE(Enterprise Edition)平台来实现。  主要特点和技术包括: 1. Servlets和JSP:     Servlets 是Java编写的服务器端程序,用于处理客户端请求和生成动态网页内容。     JSP(JavaServer Pages)