Vue插件
Vue-cli
官方网址:https://cli.vuejs.org/zh/guide/
安装vue-cli:npm install -g @vue/cli
查看版本:vue --version
单Vue开发
你可以使用vue server对单个Vue文件进行快速的原型开发,通过安装以下依赖
安装:npm install -g @vue/cli-service-global
Usage: serve [options] [entry]
在开发环境模式下零配置为 .js 或 .vue 文件启动一个服务器
Options:
-o, --open 打开浏览器
-c, --copy 将本地 URL 复制到剪切板
-h, --help 输出用法信息
eg: 运行 vue -serve A.vue;打包 vue build MyComponent.vue
项目开发
创建一个Vue项目:vue create hello-world
通过图形化界面开发:vue ui
Vue Router
官方文档:https://router.vuejs.org/zh/guide/
API:https://router.vuejs.org/zh/api/
路由基本
{
path: '/', // 路由
name: 'Home', // name
component: Home, // 组件地址,亦可component: () => import('../views/Home.vue')
meta: { title:"首页"}, // 路由元信息
alias: '/Root', // 别名,就是多个名字而已
redirect: '/login' // 重定向
children:[ // 嵌套路由
……
]
},
通过注入路由器,我们可以在任何组件内通过 this.$router
访问路由器,我们可以进行路由的跳转
-
通过path跳转:this.$router.push("/about")
-
通过name跳转:this.$router.push({name:‘About’})
-
使用this.$router.go(n):正n负n等同于浏览器的前进后退步数,0刷新当前页面
goBack() {
window.history.length > 1 ? this.$router.go(-1) : this.$router.push('/')
}
也可以通过 this.$route
访问当前路由,包括fullpath,name,mate,params,query,path等信息
路由匹配与传参
匹配所有以http://localhost:8080/header开头路径
当输入:http://localhost:8080/header/1?id=99
则通过this. r o u t e . p a r a m . i d 取 到 的 值 为 1 , 通 过 t h i s . route.param.id取到的值为1,通过this. route.param.id取到的值为1,通过this.route.query.id取到的值为99
等价于 this.$router.push({path:’/header/1’,query:{id:99}}),path不能和params 同时使用
等价于this.$router.push({name:‘Header’,params:{id:1},query:{id:99}})
等价于**<router-link :to="{ name: ‘user’, params: { userId: 123 }}">User</router-link>**
{
path: '/',
name: 'Home',
component: Home,
children:[
{
path: '/header/:id',
component: () => import('../views/Header.vue')
}
]
}
其他规则:官方说甚至可以写正则表达式,自上而下单一匹配
path: '*' // 会匹配所有路径,放在路由底部可以匹配404
path: '/user-*' // 会匹配以 `/user-` 开头的任意路径
路由嵌套
{
path: '/about',
name: 'About',
component: () => import('../views/About.vue'),
redirect: '/about/contentB',
//路由重定向,当访问/about会重定向到/about/contentB,也可以使用{ name: 'contentB' }
children:[
{
path: '', // 当访问/about的界面,与redirect冲突不能一起使用
component: () => import('../views/Bottom.vue')
},
{
path: '/about/contentA', // 也可以contentA,不带‘/’使用相对路径
name:'contentA',
component: () => import('../views/ContentA.vue')
},
{
path: '/about/ContentB',
name:'ContentB',
component: () => import('../views/ContentB.vue')
},
]
}
about界面
router-link默认使用a标签展示,使用tag可以指定标签
<template>
<div class="about">
<h1>This is an about page</h1>
<router-link to="/about/contentA" tag="div" >A</router-link> |
<router-link to="/about/contentB">B</router-link>
<router-view/> // 这是承载路由展示的地方
</div>
</template>
编程路由
声明式 | 编程式 |
---|---|
<router-link :to="..."> | router.push(...) |
//默认path,变成 /home
ruter.push('/home')
// 对象,变成 /home
router.push({ path: '/home' })
// 命名的路由,变成 /user/123
router.push({ name: '/user', params: { userId: '123' }})
// 带查询参数,变成 /register?plan=private
router.push({ path: '/register', query: { plan: 'private' }})
路由钩子
Vue提供了丰富多样的路由构造,这里我们只讲全局前置守卫和全局后置守卫,其余请查看文档
const router = new VueRouter({ ... })
router.beforeEach((to, from, next) => {
// 前置钩子...
})
router.afterEach((to, from) => {
// 后置钩子...
})
当一个导航触发时,全局前置守卫按照创建顺序调用。守卫是异步解析执行,此时导航在所有守卫 resolve 完之前一直处于 等待中。
每个守卫方法接收三个参数:
to: Route
: 即将要进入的目标 路由对象from: Route
: 当前导航正要离开的路由next: Function
: 一定要调用该方法来 resolve 这个钩子。执行效果依赖next
方法的调用参数。next()
: 进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是 confirmed (确认的)。next(false)
: 中断当前的导航。如果浏览器的 URL 改变了 (可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到from
路由对应的地址。next('/')
或者next({ path: '/' })
: 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。你可以向next
传递任意位置对象,且允许设置诸如replace: true
、name: 'home'
之类的选项以及任何用在router-link
的to
prop 或router.push
中的选项
确保 next
函数在任何给定的导航守卫中都被严格调用一次。它可以出现多于一次,但是只能在所有的逻辑路径都不重叠的情况下,否则钩子永远都不会被解析或报错。
Vuex
每一个 Vuex 应用的核心就是 store(仓库)
官方文档:https://vuex.vuejs.org/zh/
API:https://vuex.vuejs.org/zh/api/
Vuex基本
{
state // 存储数据 this.$store.state.***
mutations // 唯一可以修改state数据 this.$store.commit("***")
actions // 异步
getters // 类似与computed计算属性,通过this.$store.getters.***访问
modules // 模块化开发
namespaced?,
}
getters
Vuex 允许我们在 store 中定义“getter”(可以认为是 store 的计算属性)。就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。
getters:{
countMsg:state=>{ // 通过this.$store.getters.countMsg,注意:这里直接返回结果
return state.name + '的count为'+state.count
}
// 如何传参
countMsg2:state=>id=>{ // 通过this.$store.getters.countMsg(99),注意:这里返回函数
return state.name + '的count为'+state.count+id
}
},
mutations
更改 Vuex 的 store 中的状态的唯一方法是提交 mutation
mutation 都是同步事务
mutations: {
increment (state) { // store.commit('increment')
// 变更状态
state.count++
},
increment2 (state,payload) { // 可以接收一个载荷(参数) store.commit('increment',2)
// 变更状态
state.count+=payload // 可以使用对象作为载荷进行多参数
},
increment3 (state,payload) { // this.$store.commit({type:'increment',n:2,age:20})
// 变更状态
state.count+=payload.n // 此时载荷就是{type:'increment',n:2,age:20}
}
}
Action
Action 类似于 mutation,不同在于:
- Action 提交的是 mutation,而不是直接变更状态。
- Action 可以包含任意异步操作。
actions: {
// context包括state,getters ,commit ,rootGetters,rootState
increment (context) {
context.commit('increment')
},
increment2 ({commit}) {
commit('increment') // 使用this.$store.dispatch('increment')调用
},
increment3 (context,payload) { // 类似于mutations的载荷,dispatch('increment',100)
context.commit('increment')
},
increment 4(context,payload) { // 类似于mutations的载荷,dispatch('increment',100)
setTimeout(() => {
context.commit('increment') // 只有
}, 1000)
},
},
调用actions中的方法默认会返回一个Promise好让我们知道什么时候执行完毕,我们也可以自定义
action 中的方法可以dispatch调用自己的方法
actions: {
increment (context,paylaod) {
return new Promise((resolve,reject)=>{
setTimeout(()=>{
context.commit('increment')
resolve()
},2000)
})
}
},
methods:{
...mapActions(['increment']),
incr(){
this.increment(10).then(res=>{
console.log("success") // 异步回调
})
}
}
Module
-
我们可以将state,mutations等抽成一个单独的js文件,导入导出即可,但这并不是模块化,只是各文件独立了而已,
-
各个模块的state是独立的,但类似getters确实功能的,即使是不同的模块也不允许重复
-
如何解决上述问题,可以引入命名空间,比如a和b的setters方法都有getName同名方法,可以添加
namespaced: true
,那么该方法就变成了a/getName和b/getName this. s t o r e . g e t t e r s [ ′ a / g e t N a m e ′ ] 和 t h i s . store.getters['a/getName']和this. store.getters[′a/getName′]和this.store.getters[‘b/getName’]
const store = new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
}
})
store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态
使用系统方法快速操作
mapState返回的是一个包含方法的对象,我们可以使用对象展开...
运算符合并到当前对象中
import { mapState,mapGetters,mapMutations } from 'vuex'
computed: {
getMsg() {
return this.msg + "Vuex"
},
// 等价于 {count(){return this.$store.state.count}},注册一个count计算属性
...mapState(['count']),
...mapGetters(['countMsg'])
},
// 当我们在页面上使用{{this.$store.state.count}} 和 {{count}} 是等价的
methods:{
...mapMutations({add:"increment"}), // 亦可...mapMutations(["increment"]),不使用别名
incr(){
// this.$store.commit("increment",2)
this.add(2)
}
...mapActions(['increment']), // this.increment(10)直接调用即可
}
相关问题
为什么官方建议提交 mutation 的方式,而非直接改变 store.state.count
?
这样Vuex就能监听到每次修改state数据发生的改变,从而记录每次改变的快照,方便我们调试,如下图:如果我们直接修改的话就捕捉不到变化了
一条重要的原则就是要记住 mutation 必须是同步函数。为什么?
同上,VueX在执行完mutation 中的函数后会保存一个快照,如果是异步的话,保存的快照就与实际结果不符,使用Action就可以解决这个问题,因为Action中的是在异步请求成功之后才调用mutation 函数的,能确保保存的快照与实际结果一致