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组件中getCurrentInstance()获取App实例,但是返回null的解决方案

《Vue3组件中getCurrentInstance()获取App实例,但是返回null的解决方案》:本文主要介绍Vue3组件中getCurrentInstance()获取App实例,但是返回nu... 目录vue3组件中getCurrentInstajavascriptnce()获取App实例,但是返回n

JS+HTML实现在线图片水印添加工具

《JS+HTML实现在线图片水印添加工具》在社交媒体和内容创作日益频繁的今天,如何保护原创内容、展示品牌身份成了一个不得不面对的问题,本文将实现一个完全基于HTML+CSS构建的现代化图片水印在线工具... 目录概述功能亮点使用方法技术解析延伸思考运行效果项目源码下载总结概述在社交媒体和内容创作日益频繁的

前端CSS Grid 布局示例详解

《前端CSSGrid布局示例详解》CSSGrid是一种二维布局系统,可以同时控制行和列,相比Flex(一维布局),更适合用在整体页面布局或复杂模块结构中,:本文主要介绍前端CSSGri... 目录css Grid 布局详解(通俗易懂版)一、概述二、基础概念三、创建 Grid 容器四、定义网格行和列五、设置行

使用Python开发一个带EPUB转换功能的Markdown编辑器

《使用Python开发一个带EPUB转换功能的Markdown编辑器》Markdown因其简单易用和强大的格式支持,成为了写作者、开发者及内容创作者的首选格式,本文将通过Python开发一个Markd... 目录应用概览代码结构与核心组件1. 初始化与布局 (__init__)2. 工具栏 (setup_t

前端下载文件时如何后端返回的文件流一些常见方法

《前端下载文件时如何后端返回的文件流一些常见方法》:本文主要介绍前端下载文件时如何后端返回的文件流一些常见方法,包括使用Blob和URL.createObjectURL创建下载链接,以及处理带有C... 目录1. 使用 Blob 和 URL.createObjectURL 创建下载链接例子:使用 Blob

Vuex Actions多参数传递的解决方案

《VuexActions多参数传递的解决方案》在Vuex中,actions的设计默认只支持单个参数传递,这有时会限制我们的使用场景,下面我将详细介绍几种处理多参数传递的解决方案,从基础到高级,... 目录一、对象封装法(推荐)二、参数解构法三、柯里化函数法四、Payload 工厂函数五、TypeScript

Spring Shell 命令行实现交互式Shell应用开发

《SpringShell命令行实现交互式Shell应用开发》本文主要介绍了SpringShell命令行实现交互式Shell应用开发,能够帮助开发者快速构建功能丰富的命令行应用程序,具有一定的参考价... 目录引言一、Spring Shell概述二、创建命令类三、命令参数处理四、命令分组与帮助系统五、自定义S

Python通过模块化开发优化代码的技巧分享

《Python通过模块化开发优化代码的技巧分享》模块化开发就是把代码拆成一个个“零件”,该封装封装,该拆分拆分,下面小编就来和大家简单聊聊python如何用模块化开发进行代码优化吧... 目录什么是模块化开发如何拆分代码改进版:拆分成模块让模块更强大:使用 __init__.py你一定会遇到的问题模www.

Vue3使用router,params传参为空问题

《Vue3使用router,params传参为空问题》:本文主要介绍Vue3使用router,params传参为空问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录vue3使用China编程router,params传参为空1.使用query方式传参2.使用 Histo

Spring Security基于数据库的ABAC属性权限模型实战开发教程

《SpringSecurity基于数据库的ABAC属性权限模型实战开发教程》:本文主要介绍SpringSecurity基于数据库的ABAC属性权限模型实战开发教程,本文给大家介绍的非常详细,对大... 目录1. 前言2. 权限决策依据RBACABAC综合对比3. 数据库表结构说明4. 实战开始5. MyBA