前端学习 Vue2 笔记

173 阅读8分钟

Vue2

补充1:vue中对象修改

1.新增属性

this.$set(this.obj,"sex","女")
​
Vue.set(this.obj,"sex","女")

2.删除属性

this.$delete(this.obj,"sex")
Vue.delete(this.obj,"sex")

3.修改数组

this.$set(this.obj,sex,0,"女")
​
this.obj.sex.splice(0,1,"女")

vue-router

路由基本使用

main.js

import VueRouter from 'vue-router'
Vue.use(VueRouter)
new Vue({
    router,
})

App.vue

import VueRouter from './router'
export default {
  components: {  },
  VueRouter,
  // 路由跳转进入该组件时调用
  beforeRouteEnter (to, from, next) {
    next()//继续跳转
  },
  // 路由跳转离开该组件时调用
  beforeRouteLeave (to, from, next) {
  }
}

router/index.js

import Vue from 'vue'
import Router from 'vue-router'
import home from '@/components/home' //引入跳转所需的组件
import about from '@/components/about' //引入跳转所需的组件Vue.use(Router)
​
const router = new Router({
  mode: 'history',//hash模式有#且性能好;history相反
  routes: [
    {
      path: '/about/:id/:title',
      name: 'about',
      component: about,
      meta: {isAuth: true, title: 'about'} // 是否需要鉴权
      // props($route){
      //   return {id:$route.params.id,title:$route.params.title}
      // },
      // children: [
      //   {
      //     path: 'aboutson',
      //     component: message
      //   }
      // ]
    },
    {
      path: '/home',
      name: 'home',
      component: home,
      props ($route) { // 解构赋值{ query }:直接query.id、query.title/{query:{id,title}}:直接id、title
        return {id: $route.query.id, title: 'hello'} // params
      }
      // 独享路由守卫
      // beforeEach: (to, from, next) => {
​
      // }
    }
  ]
})
// 前置路由守卫
// router.beforeEach((to,from,next) =>{
//   if(to.meta.isAuth){//需要鉴权
//     if(localStorage.getItem('token') === 'wjj'){
//       next();
//     }else{
//       alert('权限不足!!!');
//     }else{
// next()
// }
//   }
// })
// 后置路由守卫,
// 应用:路由切换后切换窗口的图标,document.title(to.meta.title)
// router.afterEach((to,from) =>{
//   if(to.meta.isAuth){//需要鉴权
//     if(localStorage.getItem('token') === 'wjj'){
//       next();
//     }else{
//       alert('权限不足!!!');
//     }
//   }
// })export default router
​

跳转的按键:home

展示的组件:

携带参数

params参数(对象写法)
//router.js文件中的routes对象
{
    path: '/home/:id/:title',
    name: 'home',
    component: home,
}
//页面的路由to
:to='/home/1/你好'

2.对象形式,只能用name

:to="{name:"home",params:{id:1}}"//必须用name
query参数:$route.query.id
:to="/home?id=001$title=你好

2.对象形式

:to="{path:"/home",query:{id:1,title:"你好"}}"

props配置

  1. 对象形式,该对象中的所有key-value都会以props的形式传给组件,组件接收:props:['id','title']
  2. 值为布尔值,props:true,为真时,将该路由收到的params参数以props的形式传给home组件
  3. 函数,props(){return {id:1,title:"你好"}}

replace属性

给router-link标签添加replace属性,history(历史路径)中会直接被替换

编程式导航

给DOM元素添加触发事件,调用方法接收数据实现跳转和传参@click="handleClick(item)"

this.$router.push({name:"home",query:{id:1}})

this.$router.replace({name:"home",query:{id:1}})

this.router.go()this.router.go(),this.router.back()

缓存路由

包裹的保持挂载,不被销毁

<keep-alive include="Home”></keep-alive>//include属性包含的是不被销毁的组件

路由守卫

router/index.js中有代码样例

绑定class样式

image-20211102115006228

1.字符串写法:

:class="className"//class为data中的数据,用于存放此时生效的类名

2.对象写法:(常用)

:class="{样式一:true,样式二:false}"

3.数组写法:

:class="['class1','class2']"

js数组方法

1.join()

就是把数组转换成字符串,然后给他规定个连接字符,默认的是逗号( ,),书写格式:join(" "),括号里面写字符串 ("要加引号"),

2.push()和pop()

push(): 把里面的内容添加到数组末尾,并返回修改后的长度。

pop():移除数组最后一项,返回移除的那个值,减少数组的length。

3.shift()和unshift()

shift():删除原数组第一项,并返回删除元素的值;如果数组为空则返回undefined 。

unshift():将参数添加到原数组开头,并返回数组的长度 。

4.sort()

将数组里的项从小到大排序

arr.sort((a,b) => a-b)

5.reverse()

反转数组项的顺序,在原数组的基础上进行改变。

6.concat()

将参数添加到原数组中。这个方法会先创建当前数组一个副本,然后将接收到的参数添加到这个副本的末尾,最后返回新构建的数组。在没有给 concat()方法传递参数的情况下,它只是复制当前数组并返回副本。书写格式:arr.concat(),括号里面写内容 ("字符串要加引号")

7.slice()

截取数组,可以接收1-2个参数,如果只有一个参数,返回从该参数指定位置开始到当前数组末尾的所有项;如果有两个参数,该方法返回起始和结束位置之间的项——但不包括结束位置的项。

8.splice()

删除、插入和替换。

删除:指定 2 个参数:要删除的第一项的位置和要删除的项数。

插入:可以向指定位置插入任意数量的项,只需提供 3 个参数:起始位置、 0(要删除的项数为0所以是插入)和要插入的项。

替换:可以向指定位置插入任意数量的项,且同时删除任意数量的项,只需指定 3 个参数:起始位置、要替换的原数组的项数和要插入的任意数量的项。插入的项数不必与删除的项数相等。

9.indexOf()和lastIndexOf()

indexOf() 接收两个参数:查找的项、开始查找的位置;接收一个参数:只接收查找的项,默认从头查找

lastIndexOf() 接收两个参数:查找的项、开始查找的位置。其中, 从数组的末尾开始向前查找;接收一个参数:只接收查找的项,默认从尾部查找

10.forEach()

对数组进行遍历循环,对数组中的每一项运行给定函数。这个方法没有返回值直接修改原数组。

arr.forEach(( item, index, self) => {
    //item:每一项,index:索引,self:arr数组
})

11.map()

对数组进行遍历循环,对数组中的每一项运行给定函数。这个方法返回一个新数组,不修改原数组。

arr.map(item => {
    return item+1;
})

12.filter()

“过滤”功能,数组中的每一项运行给定函数,返回满足过滤条件组成的数组。这个方法返回一个新数组,不修改原数组。

arr.filter(( item, index, self) => {
    return item >= 5;
})
or
arr.filter(( item, index, self) => return item >= 5)

13.every()

判断数组中每一项都是否满足条件,只有所有项都满足条件,才会返回true。

arr = [1,2,3,4,5]
var arr2 = arr.every(function(x) {
    return x < 3;
}); //arr2 false

14.some()

判断数组中是否存在满足条件的项,只要有一项满足条件,就会返回true。

arr = [1,2,3,4,5]
var arr2 = arr.some(item => {
    return item < 3;
})//arr2 true

vue监测数据改变的原理

vue的双向绑定

核心是Object.defineProperty()方法,Object.defineProperty(obj, prop, descriptor),其中:obj为要在其上定义属性的对象,prop是要定义或修改的属性的名称,descriptor是将被定义或修改的属性描述符。

通过defineProperty()实现绑定,每当数据变化时都会调用set()方法,可以根据此来实现一个双向绑定。

DocuemntFragment(碎片化文档)

你可以把它认为是一个dom节点的容器,当你创造了10个节点,当每个节点都插入到文档当中都会引发一次浏览器的回流,也就是说浏览器要回流10次,十分消耗资源。 而使用碎片化文档,也就是说我把10个节点都先放入到一个容器当中,最后我再把容器直接插入到文档就可以了!浏览器只回流了1次。 注意:还有一个很重要的特性是,如果使用appendChid方法将原dom树中的节点添加到DocumentFragment中时,会删除原来的节点。

nodeToFragment将节点劫持

组件

组件局部引用

1.export defaul

2.import 组件名 from ‘路径‘

3.components:{组件名}

组件全局引用

1.index.js

import homelist from './homelist.vue'export default {
  install: function (Vue) {
    Vue.component('homelist',homelist);
  }
}

2.main.js

import homelist from './components/homelist/index.js'
Vue.use(homelist);

3.直接使用

vue项目文件结构

babel.config.js ES6转换为ES5

package.json 包说明书,说明项目中使用的依赖

### this.$refs

给标签添加ref属性,如:

可以通过this.$refs.form获取到这个元素,相当于原生js中的did

props传值

用于父组件向子组件传值

子组件:child.vue

props:{
    name:String,//name:{type:String,default:'lihua',required:true,}
}

父组件:parent.vue

<child :name='xiaoming'></child>

mixin混入

mixin.js文件

expfort const mixin = { //mixin一般为混入的文件名
    data() {
        return {
        }
    },
    methods:{
    },
}

.vue组件文件

import { mixin } from '路径'
mixins:[ mixin,]

directive插件

src/plugins.js

export default {
install(Vue){
    //全局过滤器
    Vue.filter('mySlice',{value => value.slice(0,4)})
    //自定义指令
    Vue.directive('fbind',{
        //类似于生命周期:指令与元素成功绑定时
        bind(element,binding){
            element.value = binding.value
        },
        //类似于生命周期:指令所在的元素插入到界面中时
        inserted(element,binding){
            element.focus()
        },
        //类似于生命周期:指令所在的模板数据更新,被重新解析时
        updata(element,binding){
            element.value = binding.value
        },
    })
    Vue.mixin({
        data(){
            return {
                x: 100,
                y: 200,
            }
        },
    })
    Vue.prototype.demo = () => {
        alert("警告!")
    }
    }
}

main.js

import plugins form './plugins'
Vue.use(plugins)

本地缓存

1.localStorage

//在localStorage中添加值
localStorage.setItem("name","xiaoming");
//存储一个对象:localStorage.setItem("name",JSON.stringify(obj));
//在localStorage中获取值
localStorage.getItem("name");
//获取一个对象:JSON.parse(localStorage.setItem("name"));
//在localStorage中获取值
localStorage.removeItem("name");
//清空localStorage
localStorage.clear();

特点:关闭浏览器后存储的东西也不会消失

2.sessionStorage

api与localStorage的相同

特点:存储的东西随浏览器窗口关闭而清除

父子组件传值

父传子

props

子传父
  1. 子组件:this.$emit("ok",this.data);父组件:@ok="handleOk" handleOk(data){}

  2. this.ref.name.ref.name.on("ok",function回调方法)

    this.ref.name.ref.name.once("ok",function回调方法)

解绑自定义事件

<button @click="offBind"></button>
offBind(){
    this.$off("ok")
}
//解绑多个事件,this.$off(["ok1","ok2"])
//解绑所有事件,this.$off()

全局事件总线

作用:任意组件之间的通信

main.js

beforeCreate(){
    Vue.prototype.$bus = this//安装全局事件总线
}

发送方组件

this.$bus.$emit("hello",666)

接收方组件

mounted(){
    this.$bus.$on("hello",(data) =>{
        console.log("接收到的数据为",data)
    })
},
beforeDestroy(){
    this.$bus.$off("hello")
},

消息订阅与发布

推荐第三方库:pubsub-js

安装:npm i pubsub-js

引入:import pubsub from 'pubsub-js'

订阅消息:

mounted(){
    this.pubId = pubsub.subscribe('hello',( msgName, data) => {//msgName:消息名,data:传递的数据
        console.log("组件发布了消息,hello消息的回调已经执行,收到消息:",data);
    })
},
beforeDestroy(){
    pubsub.unsubscribe(this.pubId)
},

发布消息:

pubsub.publish('hello',666)

this.$nextTick()

作用:规定一个回调函数,在解析模板dom节点更新后执行回调函数

### vue封装的动画效果

.come {
    animation:show 1s;
}
.go {
    animation:show 1s reverse;
}
@keyframes show{
    from {
        tranform:translate(-100%);
    }
    to {
        tranform:translate(0px);
    }
}
<template>
  <div>
      <button @click="changeAction">{{action}}</button>
      <transition name="hello" :appear="true">
          <h1 class="title" v-show="isShow">显示/隐藏</h1>
      </transition>
  </div>
</template><script>
export default {
    data() {
        return {
            action: "显示",
            isShow: false,
        }
    },
    methods:{
        changeAction(){
            if(this.action == "显示") this.action = "隐藏";
            else this.action = "显示"
            this.isShow = !this.isShow
        },
    }
}
</script><style>
.hello-enter-active {
    animation: show 1s linear;
}
.hello-leave-active {
    animation: show 1s reverse;
}
.title {
    width: 300px;
    height: 60px;
    background-color: blue;
}
@keyframes show {
    from {
        transform: translateX(-100%);
    }
    to {
        transform: translateX(0);
    }
}
</style>

重点:

  1. 包裹标签
  2. 定义css样式,.v-enter-active和.v-leave-active,在里面写animation的动画效果
  3. 给transition标签加上name属性,类名变为name-enter-active和name-leave-active
  4. transition标签加上appear属性可以自动放映动画

过度效果

.v-enter,.v-leave-to {//进入\离开的起点
    transform: translateX(-100%);
}
.v-enter-active,.v-leave-active {//进入、离开的过程
    transition: 0.2s linear;
}
.v-enter-to,.v-leave {//进入、离开的终点
    transform: translateX(0);
}

多个元素过度

其中的所有元素都必须有一个key值

引入第三方样式animate.css

<transition-group 
    name="animate__animated animate__bounce"
    enter-active-class="animate__swing"
    leave-active-class="animate__backOutUp"
    appear
>
    <h1 class="title" v-show="isShow" key="1">显示/隐藏</h1>
    <h1 class="title" v-show="!isShow" key="2">显示/隐藏</h1>
</transition-group>

ajax跨域

项目根目录vue.config.js文件

image-20211109095147368

pathRewrite: ('^/api',''),//重写请求的路径向服务器发送正确的请求路径
ws: true,//用于支持websocket
changeOrigin: false,//前端8080向后端5000发起请求,该属性为fasle时代理服务器被服务器认为是8080端口,反之则为5000

vue-resource

简介:vue1.0早期使用较多的插件库,官方目前已不再维护,交给其他团队维护。

插槽

在可复用组件中需要改变的地方加入标签,在使用组件时在组件标签中使用

标签中可以嵌套一些默认的标签,当使用者没有使用slot时出现

具名插槽

<slot name="demo">默认出现的文字</slot> //student组件
<student> //父组件使用组件student
    <input slot="demo"/>
</student>

目前插槽已经被弃用,变为v-slot:name=”message“,简写为#name,只能在template中使用

作用域插槽

父组件

<slot-demo :userObj="userObj">
       <template v-slot:footer="message"> // v-slot可以简写为#,footer是子组件中slot的name
           <div>{{message.userAge}}</div>
           <div>{{message.userName}}</div>
           //这里用的message是写组件时在slot标签上定义属性的映射。
       </template>
   </slot-demo>

子组件

// 这里的userObj是子组件数据,通过作用域插槽传给父组件使用
// 此处传给父组件的数据为{userAge: userObj.age, userName: userObj.name}
// 即为父组件中v-slot:footer="message"处的message
<slot name='footer' :userAge="userObj.age" :userName="userObj.name" ></slot>

### Vuex

原理图:

image-20211109144244426

npm i vuex

引入 Vuex import vuex from 'vuex'

应用 Vue.use(Vuex)

new store

步骤:

src/store/index.js

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const actions = {
    add(context,value){//{commit}
        context.commit('add',value)
    },
}
const mutations = 
    add(state,value){
        state.sum += value
    },
}
const state = {
    sum: 0,
}
const getters = {
    tenSum({sum}){
        return sum*10
    }
}
export default store = new Vuex.Store({
    actions,
    mutations,
    state,
    getters,
})

main.js

import store from './store'
new Vue({
    store,
})

组件.vue

this.$store.dispatch("add",2) //2是value
this.$store.getters.tenSum

mapState、mapGetters写到组件computed里面

mapActions、mapMutations写到组件methods里面

  1. 对象写法:
...mapState({sum:"sum"})
  1. 数组写法
...mapState(['sum'])
  1. 自己写
dataName(){return this.$router.state.sum}
namespace
const a = {
    namespaced:true,
    actions:{},
    mutations:{},
    state:{sum:0},
    getters:{},
}
...mapState('a',['sum'])

另一种写法:(原生手写)

this.$store.state.a.sum
this.$store.getters['a/tenSum']
this.$store.commit('a/add',n)
this.$store.dispatch('a/addWait',n)
module

项目中写法:

src/store/modules/路径下多个.js文件,每个都类似:

import Vue from 'vue'
const a = {
    namespaced:true,
    actions:{},
    mutations:{},
    state:{sum:0},
    getters:{},
}
export default a
//或者export default {}

src/store/路径下的index.js文件

import Vue from 'vue'
import Vuex from 'vuex'import app from './modules/app'
import user from './modules/user'
import getters from './getters'Vue.use(Vuex)
​
export default new Vuex.Store({
  modules: {
    app,
    user,
  },
  getters,
})

最后main.js import store from './store' 导入store/index.js文件中的所有配置

\