Vue 学习总结
vue基础语法
-
computed
定义一个值,可以依赖
data
中的多个数据属性这个值会缓存,在发生变动时才触发计算行为
computed: { // 函数 // 计算属性的一个应用点 // 计算属性就是依赖别的属性的属性 // reverseMsg -> this.msg(响应式属性) // 缓存 // 依赖多个属性 // 一对多 reverseMsg() { console.log("------------------"); // msg + count // 一定要返回一个数据的 return this.msg.split("").reverse().join("") + this.count; },
-
watch
监听data
中数据属性的变动,其他值也可以同时依赖这个属性的变动而发生变化。无法直接监听 对象 的变化,只能监听对象里的
key
的变化watch: { // 异步去请求数据的时候 // 2. 多个值去依赖一个属性的时候 // 多对一的关系 // 只观察对象里面指定的 key "user.name": { handler(val, oldVal) { console.log("name"); console.log(val, oldVal); }, }, // user: { // handler(val, oldVal) { // console.log("user"); // console.log(val, oldVal); // }, // // 深度观察 // deep: true, // }, count: { handler(val, oldVal) { console.log(val, oldVal); // todo // fetch -> data // 多个值去依赖一个属性 ,count this.hello1 = "hello1 " + val; this.hello2 = "hello2 " + val; }, // 立即执行 immediate: true, }, // count(val, oldVal) { // console.log(val, oldVal); // // todo // // fetch -> data // // 多个值去依赖一个属性 ,count // this.hello1 = "hello1 " + val; // this.hello2 = "hello2 " + val; // }, },
-
v-if
会直接操作DOM结构,如果为false,那么DOM不会渲染出element。一般用于懒加载。 -
v-show
通过CSS来控制显示和隐藏。频繁变更时使用 -
v-model
利用vue的单向数据流来实现双向绑定。 -
sync
用来解决父子组件间多个属性传值,v-model
因为会传value值,只能传递一个属性.父组件:
const app = new Vue({ el: "#app", data: { msg: "hello ", showFoo: true, }, methods: { // handleClose() { // console.log("close"); // this.showFoo = false; // }, // input v-model // eventName - input | e.target.value // input v-model xxx }, template: ` <div>root app <Foo :visible.sync="showFoo"></Foo> </div> `, });
子组件:
const Foo = { props: ["visible"], data() { return { count: 1, }; }, methods: { handleClose() { // update:[propsName] 是固定的格式 // vue -> props -> visible // this.visible = false this.$emit("update:visible", false); }, }, template: `<div v-if="visible">foo <button @click="handleClose"> close </button> <button @click="handleClick"> click </button> </div>`, };
-
slot
可以在父组件中直接对子组件中注册slot
的地方插值父组件:
const app = new Vue({ el: "#app", data: { msg: "hello ", }, template: ` <div> root app <Foo> -012 <template #first="{title}"> 123 {{title}} </template> <template #second> 456 </template> </Foo> </div> `, });
子组件:
const Foo = { data() { return { count: 1, title: "FOoFooFOo", }; }, template: `<div> <div> 默认插槽 <slot></slot> </div> <div> first <slot name="first" :title="title" age="13"></slot> </div> <div> second <slot name="second"></slot> </div> foo </div>`, };
-
mixins
可以将一些公共的逻辑抽离出来,实现复用。缺点是:1.来源不清晰;2.命名冲突
-
自定义指令
// Vue.directive 全局注册 // bind,inserted,update,componentUpdated,unbind 都是生命周期钩子 Vue.directive("focus", { bind(el, data) { // console.log(el, data); // bind init // create }, inserted(el,data) { // 插入的时候 // mounted console.log(data) el.focus(); }, // update(){ // }, // componentUpdated(){ // }, // unbind(){ // } });
Vue 组件间通信
-
props
和$emit
- 在父传子时,需要在子组件中用props声明要接收的属性,然后在父组件中传值。
父组件:
const app = new Vue({ el: "#app", data: { msg: "hello ", }, // 父组件和子组件的通信方式 template: ` <div>root app <Foo msg="abc"></Foo> </div> `, });
子组件:
const Foo = { props: { msg: { type: String, default: "a", validator(val) { // 自定义的校验器 console.log(val); return val === "abc"; }, }, }, template: `<div>foo {{msg}} <button></button> </div>`, };
- 在子传父时,需要父组件中监听子组件某一事件的变化。
父组件:
const app = new Vue({ el: "#app", data: { msg: "hello ", }, methods: { handleHeihei(val, secondVal) { console.log(val); console.log(secondVal); console.log("heihei in parent component "); }, }, template: ` <div>root app <Foo @heihei="handleHeihei"></Foo> //监听heihei事件,一旦监听到变化,触发handleHeihei方法 </div> `, });
子组件:
const Foo = { data() { return { count: 1, }; }, methods: { handleClick() { console.log("click"); this.$emit("heihei", "aaaaa", "bbbbb"); // 触发heihei事件,并发送参数 }, }, template: `<div>foo <button @click="handleClick"> // 点击触发handleClick方法 click </button> </div>`, };
-
$refs
和ref
ref
<Child ref="abc" title="parent" @toparent="handleClick"></Child> <Child ref="abc" title="parent" @toparent="handleClick"></Child> <button ref="btn" @click="handleGetChild">get child</button>
$refs
handleGetChild(){ console.log(this.$refs) // 可以取到注册ref的组件实例,并返回一个对象,包含所有注册ref的实例 const {abc}=this.$refs abc.sayHello() }
-
$parent
和$children
是严格依赖的在父组件中调用
$children
会返回由子组件返回的数组handleByChild(){ console.log(this.$children) //(2) [VueComponent, VueComponent] const child=this.$children[0] child.sayHello() },
在子组件中调用
$parent
会返回父组件,但是只会返回一个父组件。handleGetParent(){ console.log(this.$parent) //VueComponent {_uid: 2, _isVue: true, $options: {…}, _renderProxy: Proxy, _self: VueComponent, …} this.$parent.sayHi() },
-
provide
和inject
在父组件中用
provide
,可以在子组件或孙子组件甚至更深层级组件中利用inject
来接收父组件:
<comp-b ></comp-b> ------------ provide(){ return { CompATitle:"foo-----", CompA:this } },
子组件:
<comp-c></comp-c> ------------- inject:["CompATitle"],
孙子组件
<button @click="handleForCompA">handleForCompA</button> ------------------------ inject:["CompATitle","CompA"], methods: { handleGetCompA() { console.log(this.CompA); this.CompA.add(this) }, },
-
$attrs
和$listeners
$attrs
可以在子组件中没有用**props
**声明 的值,可以用inheritAttrs:false
来去除传递的DOM属性<comp-c v-bind="$attrs"></comp-c> --------------------- inheritAttrs:false, methods: { GetAttrs() { console.log(this.$attrs); } }, }
$listeners
可以在子组件中获取父组件中监听子组件的监听器。父组件:
<comp-b title="123" @a="handA" @b="handB"></comp-b>
子组件:
methods: { GetListeners(){ console.log(this.$listeners)//{a: ƒ, b: ƒ} }, },
-
EventBus是Vue的一个实例,与
props
和$emit
原理相同,但是可以在引入EventBus的组件中进行通信组件A:
import {eventBus} from "../EventBus" export default { //import引入的组件需要注入到对象中才能使用 mounted () { eventBus.$on("forCompC",this.handleForCompC);//监听子组件forCompC事件,触发handleForCompC事件 }, methods: { handleForCompC(){ console.log("handleForCompC"); }, }, }
组件C:
import {eventBus} from '../EventBus.js' export default { //import引入的组件需要注入到对象中才能使用 methods: { handleForCompA(){ eventBus.$emit("forCompC") }, }, }
-
vuex 可以将需要通信的数据放在一个文件中
-
state
username:{{user.name}} // 组件 age:{{user.age}} ---------------------- import {mapState} from 'vuex'; computed: { ...mapState(["user"]), // userName() { // return this.$store.state.user.name // } }, -------------------------- state: { // store/index.js user: { age: 18, name: "xiaohei", }, },
-
getter
十年后的年龄:{{tenYearsOld}} // 组件 ---------------------- // 全局的计算属性 getters: { //store/index.js tenYearsOld(state) { return state.user.age + 10; }, },
-
mutation
<button @click="handleChangeUserName">ChangeUserName</button> // 组件DOM <button @click="handleChangeUserAge">ChangeUserAge</button> ------------- methods: { // 组件方法 handleChangeUserName() { this.$store.commit("changeUserName","xiaohei") }, handleChangeUserAge() { this.$store.commit("changeUserAge",30) }, }, --------------------------------------- mutations: { // store/index.js // mutations changeUserName(state, payload) { state.user.name = payload; }, changeUserAge(state, payload) { state.user.age = payload; }, },
-
action 是要传递的全局异步事件
// ...mapActions相当于在组件中绑定全局事件,然后在组件中触发, ...mapActions只是传递作用 methods: { //组件 ...mapActions(["changeUserName"]) // handleFetchUsername(){ // this.$store.dispatch("changeUserName","xiaojun") //这样可以传递参数 // } }, ----------------- actions: { //store/index.js changeUserName({ commit },payload) { // 去请求后端接口 setTimeout(() => { commit("changeUserName", payload); }, 1000); }, },
-
vue-router
-
$router.replace()
和$router.push
$router.push
是将要跳转的路由推入栈中,可以进行后退。$router.replace()
是将要跳转的路由替换当前路由,无法进行后退。 -
导航守卫
导航守卫是路由跳转时调用的钩子函数。首先说一下,官方文档中完整的导航解析流程
-
导航被触发
-
在失火的组件里调用
beforeRouteLeave
守卫 -
调用全局的
beforeEach
守卫 -
在重用的组件里调用
beforeRouteUpdate
守卫 -
在路由配置里调用
beforeEnter
-
解析异步路由组件
-
在被激活的组件里调用
beforeRouteEnter
-
调用全局的
beforeResolve
-
导航被确认
-
调用全局的
afterEach
钩子 -
触发DOM更新
-
调用
beforeRouteEnter
守卫中传给next
的回调函数,创建好的组件实例会作为回调函数的参数传入。
导航守卫分为全局守卫、路由独享守卫和组件内守卫 三种。
-
全局守卫分为全局前置守卫、全局解析守卫和全局后置守卫
-
全局前置守卫
router.beforeEach
在路由跳转之前触发,主要用于登录验证,也就是路由在未跳转之前提前告知,避免跳转后再通知。
beforeEach
参数有三个(to,from,next)-
to:Route
:即将要进入的目标 路由对象 -
from:Route
:当前导航正要离开的路由 -
next:Function
:需要调用next
来resolve这个钩子-
next()
:调用next()
才可以继续向后执行下一个钩子 -
next(false)
:中断当前的导航,如果手动取消跳转,那么URL地址会重置到之前的地址,也就是from
的地址 -
next('/')
或者next({path:'/'})
:跳转到一个指定的地址。可以中断当前导航,向next
中传递replace
或push
中的选项。router.beforeEach((to, from, next) => { if (to.meta.isAuth) { // token const token = store.state.token; if (token) { next(); } else { next({ name: "Login", }); } } else { next(); } });
-
next(error)
:如果传入的参数是一个Error
实例,那么导航会被终止,并且错误会被传递给router.onError()
注册过的回调。
-
-
-
全局解析守卫
router.beforeResolve
参数和
router.beforeEach
相同。区别在于router.beforeResolve
在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后被调用。也就是说
router.beforeResolve
在afterEach
和其它守卫之间被调用。 -
全局后置守卫
router.afterEach
router.afterEach
在导航被确认之后被调用,发生在其它所有守卫之后。不会接收next
参数
-
-
路由独享守卫 即可以在路由配置中直接定义的
beforeEnter
守卫const router = new VueRouter({ routes: [ { path: '/foo', component: Foo, beforeEnter: (to, from, next) => { // ... } } ] })
beforeEnter
和beforeEach
的参数方法完全相同,在beforeEach
之后执行。 -
组件内守卫是在组件内执行的钩子函数,类似于组件内的生命周期函数,相当于为配置路由的组件添加的生命周期钩子函数。
const Foo = { template: `...`, beforeRouteEnter (to, from, next) { // 在渲染该组件的对应路由被 confirm 前调用 // 不!能!获取组件实例 `this` // 因为当守卫执行前,组件实例还没被创建 }, beforeRouteUpdate (to, from, next) { // 在当前路由改变,但是该组件被复用时调用 // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候, // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。 // 可以访问组件实例 `this` }, beforeRouteLeave (to, from, next) { // 导航离开该组件的对应路由时调用 // 可以访问组件实例 `this` } }
-
beforeRouteEnter(to,from,next)
在
beforeEach
和beforeEnter
之后调用,该守卫无法获取组件实例,this===undefined
,在beforeCreate
之前触发。next
可以接收回调函数来访问组件实例。在导航被确认的时候执行回调,并把组件实例作为回调方法的参数,可以在这个守卫中请求服务端获取数据,当成功获取并能进入路由时,调用next
并在回调中通过vm访问组件实例进行赋值等操作。beforeRouteEnter (to, from, next) { next(vm => { // 通过 `vm` 访问组件实例 }) }
-
beforeRouteUpdate(to,from,next)
在路由发生改变,且当前组件被复用时调用。
这里已经获取了组件实例,所以可以用this
beforeRouteUpdate (to, from, next) { // just use `this` this.name = to.params.name next() }
-
beforeRouteLeave(to,from,next)
用于禁止用户在未保存修改前突然离开。可以通过
next(false)
来取消。可以使用thisbeforeRouteLeave (to, from, next) { const answer = window.confirm('Do you really want to leave? you have unsaved changes!') if (answer) { next() } else { next(false) } }
-
-