Vue | 小记

MVVM

  • M Model:数据模型层 (数据)
  • V View :视图层 (页面 DOM)
  • VM ViewModel:负责监控视图层和数据模型层的数据,并对应的通知另一侧进行修改(监控者)

Vue

Vue 是一套用于构建用户界面的渐进式框架,使用 vue 必须先引入

<div class="box">
    <h1>
    	{{title}}
    </h1>
    <p>{{age}}</p>
</div>

<script>
var firstVue = new Vue({
  el: ".box",
  data: {
    title: "Vue",
    age: 5
  }
});
</script>

Vue 的模板语法

v-bind

将 data 中的数据绑定到 html 元素的属性

<p class="pic">
        <!-- 用v-bind将data中的数据绑定到html元素的属性上 -->
        <img v-bind:src="src" v-bind:alt="title" v-bind:width="width" v-bind:title="title">
        <!-- 可以简写成以下形式 -->
        <img :src="src" :alt="title" :width="width" :title="title">
    </p>

<script>
var imgVm = new Vue({
  el: ".pic",
  data: {
    src: "https://cn.vuejs.org/images/logo.png",
    title: "Vuelogo",
    width: 200
  }
});
</script>

v-if

v-if 用于控制 html 元素是否创建

<div id="app">
	<ul v-if="list.length">  <!--当满足这个条件的时候,创建ul-->
    	<li>{{list[0]}}</li> 
    </ul>    
    <p v-else>   			<!--否则,创建p-->
    	{{title}}
    </p>
    
    <p>
        <span v-if="salary<6000">当salary小于6000的时候我才创建</span>
        <span v-else-if="salary>10000">当salary大于1000的时候我才创建</span>
        <span v-else>当上面的都不满足时创建我</span>
    </p>
</div>

<script>
var vm = new Vue({
  el: "#app",
  data: {
    list: ["hello"],
    title: "v-if的用法",
    salary: 10000
  }
});
</script>

v-show

v-show 用于控制 html 元素是隐藏还是显示

<div id="app">
    <!-- v-show决定html元素是隐藏还是显示 -->
    <div v-show="isShow" style="width:300px;height:100px;border:1px solid #ccc"></div>
    <input type="button" value="显示/隐藏div" @click="isShow = !isShow"/>
</div>
 <script>
     // v-if 用于控制html元素是否创建
     // v-show 用于控制html元素显示与隐藏
var vm = new Vue({
    el:'#app',
    data:{
        isShow:true,
    }
})

v-for

用于遍历数组或者对象

<div id="app">
    <ul>
        <!-- 遍历数组 item接收的是数组元素 index接收的下标 -->
        <li v-for="(item,index) in directives">
            <span>{{index+1}}、</span> <span>{{item.name}}</span>
            <p>{{item.desc}}</p>
        </li>
    </ul>
    <hr />
    <p v-for="(item,index) in directives">
        <!-- 遍历普通对象 -->
        <span v-for="(val,key) in item">{{keyDescs[key]}}:{{val}}</span>
    </p>
    <hr />
    <p>
        <!-- v-for用户控制循环次数 -->
        <span v-for="i in count">*</span>
    </p>

    <hr />
    <div>
        <!-- template标签可以使它来包裹多个标签,并不影响html的原结构 -->
        <template v-for="(item,index) in directives">
            <h2>指令名称</h2>
            <p>指令描述</p>
            <hr />
        </template>
    </div>
</div>
<script>
// # v-for常用于:
// 遍历数组
// 遍历{}
// 控制循环次数
// template标签
var vm = new Vue({
  el: "#app",
  data: {
    count: 4,
    keyDescs: {
      id: "ID",
      name: "指令名称",
      desc: "指令作用"
    },
    directives: [
      {
        id: 1,
        name: "v-bind",
        desc: "绑定html元素的属性"
      },
      {
        id: 2,
        name: "v-show",
        desc: "控制元素显示或者隐藏"
      },
      {
        id: 3,
        name: "v-for",
        desc: "遍历数组或者对象"
      }
    ]
  }
});
</script>

v-on

vue 中的点击事件

<div id="app">
     <h3>现在人数是:{{count}}</h3>
     <!-- 绑定单击事件并且调用add方法,此方法写在methods属性中的 事件处理写的是调用函数 -->
     <p><input type="button" value="加1" v-on:click="add()"/></p>
     <!-- 绑定单击事件并且调用decrement方法,此方法写在methods属性中的 事件处理写的是函数名 -->
     <p><input type="button" value="减1" v-on:click="decrement"/></p>
     <!-- 绑定单击事件,执行count-=2的js代码 -->
     <p><input type="button" value="减2" v-on:click="count-=2"/></p>
      <!-- 以缩写方式绑定事件 @事件名="" -->
     <p><input type="button" value="加2" @click="count += 2"/></p>
    <!-- 直接写方法名时是没有办法再传递其它参数 -->
     <select @change="changeKey">
         <option value="a">A</option>
         <option value="b">B</option>
         <option value="c">C</option>
     </select>
     <!-- $event代表是当前事件对象,Vue中定义的内置变量  -->
     <select @change="changeKey2($event,'额外的参数')">
            <option value="a">A</option>
            <option value="b">B</option>
            <option value="c">C</option>
        </select>
     <p v-if="key">{{styles[key]}}</p>
 </div>
<script src="../js/vue.js"></script>
<script>
new Vue({
  el: "#app",
  data: {
    count: 40,
    key: "a",
    styles: {
      a: "red",
      b: "blue",
      c: "orange"
    }
  },
  //  methods属性中定义的都是些方法
  methods: {
    add() {
      // this 代表的是当前vue实例
      // this.count 访问到当前实例上的数据中定义的count属性
      this.count = this.count + 1;
    },
    decrement() {
      this.count--;
    },
    changeKey(event) {
      // event代表的是当前事件对象,event.target获取事件源,dom元素
      console.log(event, event.target, event.target.value);
      this.key = event.target.value;
      console.log("改变key的值");
    },
    changeKey2(msg, evn) {
      console.log(msg);
      this.key = event.target.value;
    }
  }
});
</script>

v-model

数据双向绑定 v-model

v-model.lazy:延迟视图更新,当按下回车键或者输入框失去焦点,视频是更新。不会随着输入框的内容同步更新。

<div class="box">
    <select v-model="imgs" @change="changeImg()">
        <option value="./images/1.jpg">1.jpg</option>
        <option value="./images/2.jpg">2.jpg</option>
        <option value="./images/3.jpg">3.jpg</option>
        <option value="./images/4.jpg">4.jpg</option>
        <option value="./images/5.jpg">5.jpg</option>
        <option value="./images/6.jpg">6.jpg</option>
    </select>
    <p>
        <img :src="src" alt="">
    </p>
</div>
<script>
new Vue({
  el: ".box",
  data: {
    imgs: "",
    src: ""
  },
  methods: {
    changeImg() {
      this.src = this.imgs;
    }
  }
});
</script>

数据检测更新

动态添加一个对象的属性时并不会触发视图变化(没有被监听)

可以通过 Vue.set(对象,属性名,值)

和 this.$set(对象,属性名,值)

<div id="app">
    <div v-for="(item,index) in book">
        <p><span>{{index}}:</span>{{item}}</p>
    </div>   
    <select v-model="imgSrc">
        <option value="1.jpg">1.jpg</option>
        <option value="2.jpg">2.jpg</option>
        <option value="3.jpg">3.jpg</option>
    </select>
    <input type="button" value="设置书的封面图" @click="setImg" />
</div>
<script>
var vm = new Vue({
  el: "#app",
  data: {
    imgSrc: "1.jpg",
    book: {
      id: 1,
      name: "终身成长"
    }
  },
  methods: {
    setImg() {
      //  Vue.set(this.book,'src',this.imgSrc) //动态添加某对象的属性
      this.$set(this.book, "src", this.imgSrc); //动态添加某对象的属性
    }
  }
});
</script>
PS: // 数组的方法 // 能改变原数组的方法:
push(),unshift(),shift(),pop(),splice(),reverse(),sort() ----直接触发视图更新 //
不能改变原数组的方法返回的一个的数组 :concat(),filter(),slice(),map()
----需要将返回的新数组重新赋给原数组 // 通过数组名[下标] = newValue
这样的方式是不能导致视图更新 如:
<div id="app">
    <ul>
        <li v-for="item in funcs">{{item}}</li>
    </ul>
    <input type="button" value="修改第二个元素" @click="modify()"/>
</div>
<script>
new Vue({
    el:'#app',
    data:{
        funcs:['slice','chartAt','match','split']
    },
    methods:{
        modify(){
           // 将数组的第二个元素改变为some
           #错误  this.funcs[1] = 'some'

           // 解决方案:

            //第一种: Vue.set(this.funcs,1,'some')

            //第二种:this.$set(this.funcs,1,'some')

            //第三种:this.funcs.splice(1,1,'some')

        }
    }
})
</script>

事件修饰符

事件修饰符可以连写

  • .stop 阻止事件冒泡
<div @click="console.log('div')">
    <input type="button" value="点击" @click.stop="console.log('button')"/>
</div>
  • .prevent 阻止默认事件
<a href="" @click.prevent="show">查看头相</a>
#连写
<a href="" @click.prevent.stop="show">查看头相</a>
  • .once 事件绑定一次
<a href="javascript:;" @click.once.stop="show">只能查看一次头相</a>

计算属性

计算属性放在computed里面

计算属性名是一个方法,该方法必须要返回一个值

<box id="app">
    <p>{{sum}}</p>
    <input type="text" v-model.number='x'>
     <!-- .number 将输入的内容强制转换为数值型 转换的规则是用parseInt()
	如果通过它转换结果为NaN则不转换 -->
    <input type="text" v-model.number='y'>
</box>
<script>
new Vue({
  el: "#app",
  data: {
    x: "",
    y: ""
  },
  computed: {
    sum() {
      return this.x + this.y;
    }
  }
});
</script>

watch

监听

组件 component

在使用组件时,多个单词的名字,要用横线链接。

必须先写组件,再创建 Vue 实例

template 里面写的是 html 模板,它只能包含一个 html 根元素

在组建中的 data 必须是一个函数,并且返回一个对象,这样设计的模式是保证每一个组件之间都是各自独立的,互不影响的。

props 属性值是不能被修改的(单向数据流)

创建全局组件

Vue.component('组件名',组件的配置对象) 
1.创建 
Vue.component('ThisIsHeader',{
props:[], template:`
<header>
            <img src=""/>
            <h2>
                全局组件适用于整个Vue实例
            </h2>
        </header>
` }) 
2.使用
<thisis-header></thisis-header>

属性验证

props:{
	属性名:{
		type:String, //type(String,Number,Boolean,Object,Array,Function,Promise)
    default:()=>''
	}
}

通信

父传子

  • 父组件
<template id="parent">
    <div>
        <child :message="msg"></child> 
    </div>
</template>
  • 子组件

在子组件的props中可以拿到父组件传递过来的值

  数据写法:
  props:['message']
  对象写法:
  props:{
    message:{
      type:Object
    }
  }

子传父

实现方法是父组件自定义一个事件,在子组件中$emit('父组件中自定义的事件名',需要传递的值)

+ 子组件
<div>
    <!-- 定义一个传值的方法 -->
    <input type="button" value="点击传递" @click="send">
</div>
<script>
  export default {
    data () {
      return {
        childValue: '子组件的数据'
      }
    },
    methods: {
      send () {
        // childByValue是在父组件on监听的方法
        // 第二个参数this.childValue是需要传的值
        this.$emit('parentEvent', this.childValue)
      }
    }
  }
</script>

+ 父组件
<template>
    <child @parentEvent="childByValue"></child>
</template>
<script>
  import child from './child'
  export default {
    components: {
      child
    },
    data () {
      return {
        name: ''
      }
    },
    methods: {
      childByValue:(data)=>{
        // childValue就是子组件传过来的值
        this.name = data
      }
    }
  }
</script>

非父子组件通信

主要实现方法是定义一个公共vue实例文件bus.js作为中间媒介进行传值

  //bus.js
  import Vue from 'vue'
  export default new Vue()

  /*
  * 在通信的两个组件中都需引入bus.js 
  */
  传递:
  Bus.$emit('val',this.res)
  接受:
  Bus.on('val',data=>{
    cosole.log(data)
  })

Vue实例生命周期

销毁组件或者Vue实例

# 绑定事件,然后在事件上调用clear方法
methods:{
        clear(){
            this.$destroy(true) //手动销毁vue
        }
    },

8个事件钩子

  • beforeCreate( )
  • created( ) -----能拿到实例中的data数据,在这一步即可进行ajxa请求数据
  • beforeMount( ) ----可以拿到元素,但是元素里面的数据未被解析渲染
  • mounted( )----操作DOM元素的地方,echarts应用在Vue中,Swiper
  • beforeUpdate( )
  • updated( )
  • beforeDestroy( )
  • destroyed( )

Vue组件的生命周期

  • beforeCreate( )
  • created( ) -----可以拿到props属性的值
  • beforeMount( ) ----在组件中此方法拿不到DOM元素
  • mounted( )----与Vue实例的生命周期不同,组建中需要到这一步才能拿到Dom元素
  • beforeUpdate( )
  • updated( )-----不建议在update中修改data的数据,会导致页面又重新渲染。可以选择在beforeUpdate或者watch中修改
  • beforeDestroy( )
  • destroyed( )

axios

axios是一个ajax请求的框架

get方式:axios.get( url ).then( ).catch( )

post方式:axios.post( url , data ).then( ).catch( )

  • HTML
<div id="app">
    <p><input type="text" v-model="title" /></p>
    <p><input type="text" v-model="hours" /></p>
    <input type="button" value="添加" @click="add()"/>
    <ul>
        <li v-for="(item,index) in list">
            <span>{{item.title}}</span><span>{{item.hours}}</span>
        </li>
    </ul>
</div>
  • JavaScript
 <script>
        new Vue({
            el: '#app',
            data: {
                list: [],
                title: '',
                hours: ''
            },
            methods: {
                getData() {
                    axios.get('/task/list').then(res => {
                        if(res.data.error==0){
                            console.log(res);
                            this.list = res.data.data.list
                        }
                    })
                },
                add() {
                    axios({
                        url: "/task/add",
                        method: 'post',
                        data: {
                            title: this.title,
                            hours: this.hours
                        }
                    }).then(res => {
                        this.getData()
                    }).catch(err => {

                    })
                }
            },
            created() {
                this.getData()
            }

        })
    </script>
  • axios统一处理请求或响应

统一集中处理接口传入相同参数的设置以及统计处理错误码的设置

// 设置请求的根路径
axios.defaults.baseURL = 'http://localhost:9000/';
// 设置ajax请求超时时间
axios.defaults.timeout = 2000
// 设置请求拦截器
axios.interceptors.request.use((config)=>{
    // 在此可以设置所有接口都需要用到的参数
    // console.log('interceptors.request',config)
    config.headers['usertoken'] = '299429942428423191031031';
    let joinStr = config.url.indexOf('?') === -1 ?  '?' : '&';
    config.url += joinStr + 'v1=1.0'
    return config
})
// 设置响应拦截器
axios.interceptors.response.use(result => {
    console.log('请求成功时得到的数据',result)
    return result.data
},error=>{
    if(error.message.includes('timeout')){
        alert('请求超时')
        return Promise.reject('timeout');
    }
    return Promise.reject(error);
});

ref属性

$refs--通过它可以获取到html元素或者组件,

this.$refs.html标签的ref属性值 ---获取到对应的dom元素

循环出来的元素上加了ref属性时,通过this.$refs.ref值是可以全部获取的(获取出来的是个数组)

过滤器

  • 全局自定义过滤器
# 定义
Vue.filter('过滤器名称',(value)=>{
    
})
# 使用
<P>{{name|过滤器名称}}</P>
  • 局部过滤器
// 在组件的选项中定义
filters: {
  capitalize: function (value) { //过滤器名:capitalize
    if (!value) return ''
    value = value.toString()
    return value.charAt(0).toUpperCase() + value.slice(1)
  }
}

自定义指令

除了核心功能默认内置的指令 (v-model 和 v-show),Vue 也允许注册自定义指令,有时候需要对普通DOM元素进行底层操作,这时候就会用到自定义指令。

  • 注册全局指令
Vue.directive('指令名',{
    //内置三个钩子函数bind,inserted,update
    bind(el,binding,vnode){
        
    },
    inserted(el,binding,vnode){
        
    },
    update(el,binding,vnode){
        
    }
})
  • 注册局部指令
// 在组件的选项中定义
directives: {
  focus: {	//指令名:focus
    inserted: function (el,binding,vnode) {
      // 指令所绑定的元素,可以用来直接操作 DOM 。
      // binding 一个对象,所包含属性参考官网,其中有一个value属性获取到使用指令时传递的参数
      el.focus()
    }
  }
}

// 在模板中使用方式:<input v-focus />

Vue-cli

  • 全局安装vue-cli
npm install @vue/cli -g
  • 新建一个项目
# 在存放项目的地方打开终端
 1.vue create ProjeceName(项目名称)
 2.选择Manually select features
 3.勾选自己想要的包(Babel,Router,Vuex,css pre-processors,Linter/Formatter)
 4.Sass/scss(with node-sass)
 5.standard
 6.Lint on save
 7.ted config files
 
  • 查询npm来源以及更改成淘宝镜像
查询:npm get registry
更改成淘宝镜像: npm set registry="http://registry.npm.taobao.org"
  • proxy代理配置
// 在vue.config.js中添加以下代码
devServer:{
    port:3000,
    '/api':{
        target:'http://xxxx.com/',
        changeOrigin: true,
        pathRewrite: {  //路径重写
            '/api': '' 
        }
    }
}

动态路由

在定义路由时路径中带有冒号的即是动态路由,例如路径是 /film/:id 为动态路由,通过this.$route.params.id 获取到相应的值。

子路由/嵌套路由

在路由里在嵌套子路由

export default new Router({
  routes: [{
      path:'/',			//重定向:当访问的是根目录的时候,跳转到/films路径
      redirect:'/films'
  },{
      path: '/films',
      name: 'home',
      component: films,
      children:[{  	//子路由
          path:'',
          redirect:'/films/nowplaying'  //重定向:当访问/films时,直接跳到/films/nowplaying
      },{
          path:'nowPlaying',
          component:NowPlaying
      },{
          path:'comingSoon',
          component:ComeSoon
      }]
 },{
     path:'/center',
     component:Center
           }]
})

编程式导航

this.$router.push('路径' )
this.$router.push({path:'路径',query:{参数名:值}})
this.$router.push({name:'路由名称',params:{参数名:值}})
// 注意:this.$router.push方法的参数是对象时,path不能与params搭配使用
// 使用params带参数,需要对应使用name来指明路径,query带参数,则path,name都可以
// 获取动态路由中的参数使用this.$route.params
// 获取路径中?部分的参数使用this.$route.query
# 例如
// /article/11 ===> this.$route.params.xx
// /article/detail?id=11====>this.$router.query.id

路由守卫

  • 路由全局守卫 (全局前置守卫(全局拦截))
const router = new VueRouter({ ... })
router.beforeEach((to,from,next)=>{
    //to 是即将转向的路由对象
    //from 是从哪儿来的路由对象
    //next是一个函数,继续执行,即验证通过next,否则拦截
})
  • 单个路由守卫 (单个拦截)

const router = new VueRouter({
  routes: [
    {
      path: '/foo',
      component: Foo,
      beforeEnter((to,from,next)=>{
      //to 是即将转向的路由对象
      //from 是从哪儿来的路由对象
      //next是一个函数,继续执行,即验证通过next,否则拦截
      })
    }
  ]
})
  • 组件内部的路由守卫 (单个拦截)
beforeRouteEnter (to, from, next) {
    // 在渲染该组件的对应路由被 confirm 前调用
    // 不!能!获取组件实例 `this`
    // 因为当守卫执行前,组件实例还没被创建
    next(vm => {
    	// 通过 `vm` 访问组件实例
  	})
},
beforeRouteUpdate (to, from, next) {
    // 在当前路由改变,但是该组件被复用时调用
    // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
    // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
    // 可以访问组件实例 `this`
},
beforeRouteLeave (to, from, next) {
    // 导航离开该组件的对应路由时调用
    // 可以访问组件实例 `this`
}

监听路由

监听路由的变化,当路由发生变化时要执行的操作

watch:{
    '$router':{
        handler(newVal){
            //重新请求调用 当前路由发生变化时将调用该方法,如/user/1切换到/user/2时
        },
        deep:true,
        immediate:true //页面初始化完成立即监听
	}
}

路由懒加载

只有访问该组件对应的路径时,才将相对应的js文件打包。按需加载

export default new Router({
  routes: [
    {
      path:'/active',
      component: () => import('./views/active.vue')
    },
    {
      path:'/center',
      component: () => import('./views/center.vue')
    }
  ]
})

Vuex

各组件之间的数据共享(改变共享数据是需要符合预设的规则才能被修改)

在一个组件中导出了多个方法,在导入的时候要加花括号

  • state
mapState是一个方法可以简便获取store中的state
mapGetters是一个方法可以简便获取store中的getters
import {mapState,mapGetters} from 'vuex' //用此方法在组件中渲染的时候则用{{xxx}},否则用{{$store.state.xxx}}
export default {
  name: 'home',
  computed:{
    ...mapState(['total','username']),
    ...mapGetters(['totalMsg'])
  },
  components: {
    
  }
}
var a = [10,20]
var b = [...a,33,44]
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
// 状态管理中心
let store = new Vuex.Store({
  // 数据中心
  state:{
    total:10,  // 水的库存(桶)
    username:'admin'
  },
  // 同步行为(只有mutation中的方法才能修改state中的数据)
  mutations:{
    addTotal(state,val){
      // state接收的是当前状态管理中心的数据中心
      console.log('同步行为addTotal',state,val)
      state.total += val
    },
    reduceTotal(state,val){
      console.log('同步行为reduceTotal',state,val)
      state.total -= val
    }
  },
  // 异步行为(异步行为是不能直接修改state,只能提交给mutaions中的方法来执行)
  actions:{
    ybAddTotal(store,val){
      // store变量代表是当前状态管理中心
      console.log('异步行为ybAddTotal',store,val)
      setTimeout(()=>{
        // 成功了则提交mutations中的方法
        store.commit('addTotal',val)
      },3000)
    },
    ybJianTotal(store,val){
      setTimeout(()=>{
        // 成功了则提交mutations中的方法
        store.commit('reduceTotal',val)
      },2000)
    }
  },
  // 一些变量,变量名作为方法,方法一定要返回一个值,根据现有state中某个变量得来的结果
  getters:{
    totalMsg(state){
      // state接收的是当前状态管理中的state
      return state.total > 20 ? '' : '库存不足'
    }
  }
})

export default store
posted @ 2022-06-29 22:50  ^finally  阅读(43)  评论(0)    收藏  举报