vue笔记

一、Vue基础

1、vue初探

1)我们使用的数据需要现在data中存在;数据要先存在,才能实现数据绑定。

2)通过索引的方式、改变长度的方式去改变数组,都不能渲染视图;

数组变异方法:push,pop,shift,unshift,sort,reverse,splice。

3)$set:vm.$set(vm.obj, ‘xxx’, 90); $nextTick:dom循环结束触发的回调函数;

$el:拿到vue挂载的元素; $mount:选择vue挂载到的dom元素。

改变vue里面的值是异步操作,要拿到异步后的数据得用$nextTick

2、vue指令

1)v-pre:vue渲染时自动跳过;v-cloak:记录还没渲染,可使[v-cloak]display:none; v-once:只认第一次的渲染;

v-html === innerHTML,v-text === v-innerText,注意XSS攻击。

2)v-if(v-else、v-else-if):控制显示与隐藏,可配合template标签(临时标签、不渲染)使用;

v-show:控制显示与隐藏,但其一直存在,只是改变其display为none;v-if则是完全移除dom元素,且v-show不支持template。

3)v-bind(:):绑定属性。如 或;

绑定两个属性值时需要用数组或对象,如 :class="[redClass, blueClass, flag && red]";

:style="{widh: ‘100px’, height: ‘100px’, backgroundColor: color}" 。注意style < :style。

4)v-on(@):绑定事件。如 @click=“change(‘red’)” ;

注意methods里面不能写箭头函数,否则this指向windows。且事件名称不能和data里面的一样。

5)v-for:遍历渲染dom,要有对应的key值(浏览器会参照渲染前后key值有没有变化决定要不要重新渲染),key值最好不要用index。

6)v-model:双向数据绑定;input text,textarea。input checkbox、radio、select是把value值实现双向数据绑定。v-model.lazy(延迟绑定),v-model.number(转为number),v-model.trim(去除前后空格),

7)避免使用JavaScript中字符形式的一元操作符(delete、void、typeof)作为属性名。如:@click=“delete(index)”。

8)v-slot (#) : 写组件时用的插槽。例子:

<div id="app">
    <my-button>
        啦啦啦
        <template v-slot:right>
            呵呵呵
        </template>
        哈哈哈
    </my-button>
</div>
Vue.component('myButton', {
    template: `<button>
        <slot></slot>
        -----------------
        <slot name="right"></slot>
        </button>`
});

9)vue自定义指令(directive):全局的如下定义,局部的则定义在vue实例内。

el:指令绑定的元素;

bindings:指令包含的信息(参数,修饰符),如v-slice:9.number;

vnode:vnode.context代表vue的实例;

bind():绑定时执行;update():更新时执行;insered():把dom元素插入到页面时执行。直接写表示bind和update结合。例子:

<div id="app">
<input type="text" v-slice:9.number="content"/> {{content}}
</div>
// Vue.directive('slice', (el, bindings, vnode) => {
    // const val = bindings.value.slice(0, 5);
    // vnode.context.content = val;
// });
Vue.directive('slice', {
	bind(el, bindings, vnode) {       
		const context = vnode.context;       
        const length = bindings.arg || 5;       
        const numberFlag = bindings.modifiers.number;       
        let initVal = context[bindings.expression];       
        if(numberFlag) {           
            initVal = initVal.replace(/[^0-9]/g, "");       
        }       
        initVal = initVal.slice(0, length);       
        el.value = initVal;       
        context[bindings.expression] = initVal;       
        el.oninput = e => {           
            let value = e.target.value;           
            if(numberFlag) {               
                value = value.replace(/[^0-9]/g, "");           
            }           
            let val = value.splice(0, length);           									context[bindings.expression] = val;           
            el.value = value;       
        }   
    },   
    update(el, bindings, vnode) {       
        let context = vnode.context;       
        const numberFlag = bindings.modifiers.number;      
        let value = context[bindings.expression];      
        if(numberFlag) {           
            value = value.replace(/[^0-9]/g, "");      
        }      
        el.value = value;       
        context[bindings.expression] = value;   
    },    
    inserted() {       
        el.focus();    
    }
});


const vm = new Vue({    
    el: '#app',    
    data: {        
        content: ''    
    }
});
3、过滤器(filter):局部的优先级大于全局的。
<div id="app">{{money | toMoney(2)}}</div>
Vue.filter('toMoney', (value, times) => {    
    console.log('vue');    
    return (value * times).toLocaleString();
});
const vm = new Vue({    
    el: '#app',    
    filters: {      
        toMoney: (value) => {           
            console.log('app');       
            return (value).toLocaleString();     
        }   
    },   
    data: {      
        money: 10000000,  
    }
});
4、挂载

在这里插入图片描述

const vm = new Vue({   
    el: '#app',   
    template: '<h2>{{msg}}</h2>',  
    render() {       
        const tag = "div";      
        return (        
            <tag class="haha" style={{color: 'red', font-size: '12px'}}                on-click={() => {console.log('haha')}}>               
                思考              
                <p>哈哈</p>       
            </tag>       
        )    
    },    
    data: {      
        msg: '哈哈哈'   
    }
});
5、生命周期

1)可以在created()里调用数据、ajax,mounted()里面拿到$el、调ajax;

2)不要在updated()里面更改数据,会造成死循环;

3)可以在beforeDestroy()里面清除定时器;

在这里插入图片描述

6、计算属性(computed)和侦听器(watch)

1)前提:data的this指向window,methods在每次改变数据的时候都会执行,消耗性能,所以需要computed或者watch;

2)computed没办法执行异步的操作;

3)优先级:data > methods > computed。

const vm = new Vue({   
    el: '#app',  
    data: {      
        name: 'scj',    
        age: 18,      
        person: ''   
    },   
    // mounted() {    
    //     this.getPerson();  
    // },  
    methods: {   
        getPerson() {     
            this.person = `姓名:${this.name},年龄:${this.age}`;    
        }    
    }, 
    computed: {       
        // person() {       
        //     return `姓名:${this.name},年龄:${this.age}`  
        // }   
    },    
    watch: {      
        // name () {     
        //     this.getPerson();      
        // },     
        // age () {          
        //     this.getPerson();    
        // },
        name: {
            handler (newVal) {
                this.getPerson();
            },
            immediate: true,  //是否直接渲染
        },
        age: {
            handler (newVal) {
                this.getPerson();
            },
        }
    }
});

computed应用:

<div id="app">  
    初始值:<input type="text" v-model.number="firstNum"> + <input type="text" v-model.number="secondNum">    
    计算值:<input type="text" v-model.number="sum">
</div>
const vm = new Vue({   
    el: '#app',   
    data: {      
        firstNum: null,     
        secondNum: null,  
    },    
    methods: {  
    },    
    computed: {  
        sum: {  
            get () {   
                return this.firstNum + this.secondNum;    
            },        
            set (val) {     
                this.firstNum = this.secondNum = val / 2;        
            }      
        }    
    },
});
7、组件
1)数据传递&属性校验
<div id="app">   
    <my-content :content="content" :title="title"></my-content>
</div>
const vm = new Vue({
            el: '#app',
            data: {
                content: '组件间数据的传递与校验',
                title: '组件'
            },
            components: {
                myContent: {
                    data() {
                        return {
                            ...
                        }
                    }
                    props: {
                        content: {
                            type: String,
                            default: '内容默认值',
                        },
                        title: {
                            type: String,
                            validator (value) {
                                return value === '组件';
                            },
                            required: true
                        },
                        arr: {
                            type: Array,
                            // 数组是引用值,直接指向的话容易混乱,要用函数return
                            default: () => [1, 2, 3]    
                        },
                    },
                    template:
                        `<div>
                                {{arr}}
                                <h3>{{title}}</h3>
                                <h4>{{content}}</h4>
                           </div>`,
                }
            }
        });
2)父向子传递数据

$attrs:表示从父组件传过来且未被注册的数据集,可直接传给自身子组件,如:v-bind="$attrs",但是未被注册的属性会显示在行间,可通过inheritAttrs:false来取消显示。

② $parent,$children:获得父组件或子组件实例。

③ provide、inject:父组件中使用provide{ title: ‘我是标题’ },子或孙组件中使用inject: [‘title’]。

3)子向父传递数据

① 引用(ref):如对dom元素加属性ref=“parentDom”,可通过this.$refs.parentDom拿到该dom元素。

② 通过函数传递:如对dom元素追加属性func=“func”,子组件中调用该函数传值。

③ $listeners:如对dom元素加@click=“func”,在子组件中通过this.$listeners.click(this.msg)即可向父组件传值。在子组件中用v-on="$llisteners"监听所有事件。

④ $emit:接第③例子前半段,如$emit(‘click’, this.msg)。

4)兄弟组件间传递数据

event bus

Vue.prototype.bus = new Vue();
this.bus.$emit('click', this.msg);// 要发送数据的组件触发事件并带数据

this.bus.$on('click', content => {// 其它组件监听事件触发并接受数据
    this.content = content;
})
5)组件间单向数据流

子组件最好不要更改父组件的传过来的值,避免传过来是对象的话把父组件的值也改了。可先将传过来的值赋给子组件本身的值再更改,但也得注意传过来的是对象。

6)组件间双向数据流

v-model = :value + @input。

② .sync = :value + @update:value。

8、安装脚手架

npm install -g @vue/cli 安装脚手架,用于生成项目
npm install -g @vue/cli-service-global 快速原型开发 编译.vue文件

如果之前已经安装过旧版本(非3.x)脚手架,需先卸载旧版本:
npm uninstall vue-cli -g

如果仍然需要使用旧版本的 vue init 功能,可以全局安装一个桥接工具:
npm install -g @vue/cli-init 拉取旧版本

插件名字:Vetur(VS code)

9、vue文件

运行vue serve 时必须具备入口文件,入口文件可以是:main.js, index.js, App.vue or app.vue。

<template>		// 相当于component里的template
    
</template>

<script>
    export default {	// 这层里面的相当于除了template以外的东西
        name: ""		// name属性可以让组件调用自身组件,实现组件递归
    }
</script>

<style scoped>		// scoped属性约束了style的作用域

</style>
10、利用脚手架搭建项目

1)vue create vue_app 更改用户文件夹下的.vuerc文件可以更改预设

2)vue ui 更改用户文件夹下的.vue-cli-ui/db.json文件可以更改预设

11、路由

创建路由:命令行vue add router

1)router-link

router-view:路由入口处,tag="li"可以改变router-link为li标签

<router-link tag="li" to="/about">About</router-link>
<router-link tag="li" :to="{name: 'about'}">关于</router-link><router-link tag="li" :to="{path: '/community'}">社区</router-link>
2)路由器配置
import Vue from 'vue';
import Router from 'vue-router';
import Home from './views/Home';

Vue.use(Router);  // 使用路由,不写则路由不生效

export default new Router({
  mode: 'history',  // 取消hash型(路径上的#会消失)
  linkExactActiveClass: 'active-exact',   // 完全匹配路由路径,这里是重新配置其名字
  linkActiveClass: 'active',  // 不完全匹配路径,这里是重新配置其名字
  routes: [
    {
      path: '/home',
      name: 'home',
      component: Home
    },
    {
      path: '/learn',
      name: 'learn',
      // 路由懒加载
      component: () => import('./views/Learn')
    },
    {
      path: '/student',
      name: 'student',
      component: () => import('./views/Student')
    },
    {
      path: '/about',
      name: 'about',
      component: () => import('./views/About')
    },
    {
      path: '/community',
      name: 'community',
      component: () => import('./views/Community'),
      redirect: '/community/academic',  // 重定向路径
      // 路由嵌套
      children: [
        {
      // '/academic'带/的话就得写全路径,不带就自动向上找带/的,并拼接路径
          path: 'academic',
          name: 'academic',
          component: () => import('./views/Academic')
        },
        {
          path: 'download',
          name: 'download',
          component: () => import('./views/Download')
        },
        {
          path: 'personal',
          name: 'personal',
          component: () => import('./views/Personal')
        }
      ]
    },
    {
      path: '/NotFound',
      component: () => import('./views/NotFound')
    },
    {
      path: '*',        // *代表上面没匹配到的路径
      redirect (to) {   
        console.log(to);  // to 表示路径的相关信息
        if (to.path === '/') {
          return '/home';
        } else {
          return '/NotFound';
        }
      }
    }

  ]
})

3)$router跳转、$route

① $router记录的是路由的整体信息,常借助它来实现跳转

this.$router.push('/home');     // 在当前添加一个
this.$router.push({				// 动态路由跳转
                    name: 'question',
                    params: {	// 或query
                        id: this.questionId
                    }
                })
this.$router.replace('/home');  // 取代当前路由
this.$router.go(-1);		// 回退

② $route记录的是路径信息,常用它来取动态路由传过来的值,如this.route.params.id。

4)路由守卫
组件路由守卫

① beforeRouteLeave 路由离开前会执行的函数(组件路由改变前)

beforeRouteLeave(to, from, next) {
            if (this.name) {
                const flag = window.confirm('你确定要离开吗');
                if (flag) {
                    this.name = '';
                    next();
                }
            } else {
                next();
            }
        }

beforeRouteEnter 路由进入前会执行的函数,这个函数里面的this为undefined,因为路由还没进入。可在next函数中使用this(vm)。

beforeRouteEnter(to, from, next) {
            next(vm => {
                console.log(vm);
            });
        },

beforeRouteUpdate 路由更新前会执行的函数,常用来检测动态路由更新,可配合mounted使用,第一次进来用mounted渲染组件,不刷新组件的情况下监测路由更新去重新渲染组件。

beforeRouteUpdate(to, from, next) {
            this.getData(to.params.id);
            next();
        },

注意 beforeRouteEnter 是支持给 next 传递回调的唯一守卫。对于 beforeRouteUpdatebeforeRouteLeave 来说,this 已经可用了,所以不支持传递回调,因为没有必要了。

独享路由守卫
{
    path: '/home',
        name: 'home',
        component: Home,
        beforeEnter(to, from, next){
           next();
        }
},
全局路由守卫
router.beforeEach((to, from, next) => {		// 进来前
  next();
});
router.beforeResolve((to, from, next) => {	// 全部解析完成前
  next();
});
router.afterEach(() => {					// 全部加载完以后
  next();
});
导航解析流程
5)动态路由

① 用:设置动态路由(http://localhost:8080/question/201804):

path:'/question/:id'
<router-link
             tag="li"
             :to="{name: 'question', params: {id: item.questionId}}"
</router-link>
this.$route.params.id;

② 用?设置动态路由(http://localhost:8080/question?id=201804):

path:'/question'
<router-link
             tag="li"
             :to="{name: 'question', query: {id: item.questionId}}"
</router-link>
this.$route.query.id;
6)路由元信息

存储路由信息,可检测路由里面是否有元信息来判断能否登录,配合cookie

{
    path: '/student',
    name: 'student',
    meta: {
        login: true
    },
    component: () => import('./views/Student')
}
12、vue-cli配置

1)打包项目: npm run build

2)安装less和less预处理器:npm install less less-loader -D

3)安装全局样式插件:vue add style-resources-loader

4)vue.config.js配置文件

const path = require('path');
module.exports = {
    // 不打包.map文件
    productionSourceMap: false,

    // 设置输出目录
    outputDir: './myDist',

    // 根据环境变量设置资源路径
    publicPath: process.env.NODE_ENV === 'production' ? 'http://www.baidu.com' : '/',

    // 将文件都打包整合到assets里面
    assetsDir: 'assets',
    
	// 在webpack基础上再增加一些自己的配置,config表示mudule.exports
    chainWebpack: config => {   
        // 配置文件夹别名,这里是把views的名字改为_v
        config.resolve.alias.set('_v', path.resolve(__dirname, 'src/views'))
    },
    
	// webpack常规配置
    configureWebpack: {         
        // plugin: [],
        // module: {}
    },
	
    // 服务器代理配置,可实现跨域
    devServer: {
        proxy: {                
            '/api/chat/sendMsg': {
                target: 'http://api.duyiedu.com'
            }
        }
    },

    pluginOptions: {
      'style-resources-loader': {
        preProcessor: 'less',
        patterns: [
            // 设置全局样式的路径
            path.resolve(__dirname, 'src/assets/styles/variable.less')
        ]
      }
    }
};

二、Vuex

1、描述

当页面有很多组件的时候,采用上面的方法共用和传递数据很麻烦,就有了vuex,vuex相当于一个公共的仓库,管理着组件共用的数据,能让组件间传递数据更容易。

2、安装

命令行执行 vue add vuex 成功后项目目录会多个store.js文件

3、state
// 当代码开发的时候开启严格模式,规范代码;当项目进入生产上线时关掉严格模式,提升性能
strict: process.env.NODE_ENV !== 'production',

拿store里面的数据:

this.$store.state.xxx			// 不严格模式
import {mapState} from 'vuex';
computed: {
    // ...mapState(['studentList']),		// 直接取
    ...mapState({
    	myStudentName: state => state.studentList	// 改变量名,防止冲突
    })
}
4、getters

类似computed,因为直接改变state里面的数据并不能重新渲染组件,所以用getters。

getters: {
	func(state, getters) {	// 通过state.xxx能拿到state里面的值,getters同理
        return 'xxx';
    }
}

拿getters里面的数据:

this.$store.getters.xxx			// 不严格模式
import {mapGetters} from 'vuex';
computed: {
    // ...mapGetters(['studentList']),		// 直接取
    ...mapGetters({
    	myStudentName: 'studentList'	// 改变量名,防止冲突
    })
}
5、mutations

改变vuex中的状态,只能执行同步的

mutations: {
    changeStudentList (state, {tempObj, name}) {
      // setTimeout(() => {
        state.studentList.push(tempObj);
        state.name = name;
      // }, 1000)
    }
  },

使用mutations:

this.$store.commit('xxx')		// 不严格模式
import {mapMutations} from 'vuex';
computed: {
    // ...mapMutations(['xxx']),		// 直接取
    ...mapMutations({
    	newXXX: 'xxx'	// 改变量名,防止冲突
    })
}
6、actions

提交mutations,让mutations去更改状态,能够执行异步的

actions: {
    changeStudentList ( {commit}, payload ) {
      setTimeout(()=>{
        commit('changeStudentList',payload )
      }, 1000)
    }
  }

使用actions:

this.$store.dispatch('xxx')		// 不严格模式
import {mapActions} from 'vuex';
computed: {
    // ...mapActions(['xxx']),		// 直接取
    ...mapActions({
    	newXXX: 'xxx'	// 改变量名,防止冲突
    })
}
7、Module
  • 根据功能让vuex分出模块,可以在store.js里面分,也可以分出文件来
  • state会放入到每一个模块下,getters、mutations、actions会直接放入到全局
获取vuex中的数据(无namespaced)
  • 获取state : this.$store.state.moduleName.xxx
  • 获取getters: this.$store.getters.xxx
  • 获取mutations: this.$store.commit('xxx')
  • 获取actions: this.$store.dispatch('xxx')
  • 可以通过mapXXX 方式拿到getters、mutations、action,但是不能拿到state,如果想通过这种方式获取state,需要加命名空间:namespaced:true
获取vuex中的数据(有namespaced)
  • 获取state : this.$store.state.moduleName.xxx
  • 获取getters: this.$store['moduleName/getters'].xxx
  • 获取mutations: this.$store.commit('moduleName/xxx')
  • 获取actions: this.$store.dispatch('moduleName/xxx')
  • 可以通过mapXXX: mapXXX('moduleName', ['xxx']) mapXXX('moduleName', {})

三、单元测试

1、测试分类

1)TDD: Test Driven Development 测试驱动开发

2)BDD: Behavior Driven Development 行为驱动开发

2、测试工具

建立测试的文件最好放在test/unit目录下,命名的时候必须以xxx.spec.js或xxx.test.js结尾

1)mocha & chai
  • mocha:测试框架

    vue脚手架内部就安装好了,不需要再次引入

    自己安装:mocha mocha-webpack

  • chai:断言库,断定左边的和右边的是否相等

    三种断言风格:should、expect、assert

    www.chaijs.com

用法
  • 套件

    describe(‘套件名字’, () => {})

    生命周期

    before(()=>{}) 调用该套件的所有测试用例之前执行且只执行一次

    after(()=>{}) 调用该套件的所有测试用例之后执行且只执行一次

    beforeEach(()=>{}) 调用该套件的每个测试用例之前执行,有几个测试用例就执行几次

    afterEach(()=>{}) 调用该套件的所有测试用例之后执行,有几个测试用例就执行几次

  • 用例

    it(‘用例名字’, () => {})

    done

    done函数被调用的时候,才能完成测试

chai的基本用法
  • 判断相等

    判断基本类型 expect(1).to.be.equal(1);

    判断引用类型:expect({a: 1}).to.be.deep.equal({a: 1}) / expect({a: 1}).to.be.eql({a: 1})

    deep标记:该标记可以让其后的断言不是比较对象本身,而是递归比较对象的键值对

  • 判断不等

    expect(2).to.be.not.equal(1);

  • 判断大于

    expect(10).to.be.above(5);

    expect(10).to.be.greaterThan(5);

  • 判断小于

    expect(5).to.be.below(10);

    expect(5).to.be.lessThan(10);

  • 判断大于等于

    expect(10).to.be.at.least(10);

    expect(10).to.be.not.lessThan(10);

  • 判断小于等于

    expect(5).to.be.at.most(5);

    expect(5).to.be.not.greaterThan(5);

  • 判断长度

    expect([1, 2, 3]).to.be.lengthOf(3);

  • 判断为truthy,(除了false、undefined、null、正负0、NaN、""的值)

    expect(1).to.be.ok;

  • 判断为true、false、null、undefined、NaN

    expect(true).to.be.true;

    expect(false).to.be.false;

    expect(null).to.be.null;

    expect(undefined).to.be.undefined;

    expect(NaN).to.be.NaN;

  • 判断包含

    expect(‘shanshan’).to.be.include(‘s’); 包含

    expect(‘shanshan’).to.be.contain(‘s’); 包含

    expect(‘shanshan’).to.be.match(/s/); 匹配

测试函数代码示例:
import { abs, add } from "../../src/count";
import { expect } from "chai";

describe('add函数', () => {
    it('测试给add传入几个值,期待返回值为这几个值的和', () => {
        expect(add(1, 5, 7)).to.be.equal(13);
    });
});

describe('abs函数', () => {
    it('测试给abs传入正数,期待返回值与输入相同', () => {
        expect(abs(1)).to.be.equal(1);      // 测试基本类型数据
        // expect({}).to.be.deep.equal({});    // 测试对象、引用值等,deep.equal可写成eql
    });

    it('测试给abs传入负数,期待返回值与输入之相反', () => {
        expect(abs(-1)).to.be.equal(1);
    });

    it('测试给abs传入0,期待返回值是0', () => {
        expect(abs(0)).to.be.equal(0);
    });

    it('测试给abs传入非数值,期待返回值是NaN', () => {
        expect(abs("")).to.be.eql(NaN);
        expect(abs([])).to.be.eql(NaN);
        expect(abs(null)).to.be.eql(NaN);
        expect(abs({})).to.be.eql(NaN);
        expect(abs(undefined)).to.be.eql(NaN);
    });
});

测试dom元素渲染示例
import HelloWorld from '@/components/HelloWorld';
import Vue from 'vue';
import { expect } from 'chai';
import { mount } from '@vue/test-utils';

// Vue.component({})

// const vm = new Vue({})
// vm.$el == > 挂载的元素  vm.$el.  h1 innerHTML

// Vue.extend

// vue test units

describe('HelloWorld.vue', () => {
  it('测试msg属性,能否正常渲染, 原生', () => {
    const msg = 'hello world';
    const Constructor = Vue.extend(HelloWorld);
    const vm = new Constructor({
      propsData: {
        msg
      }
    }).$mount();
    const domInner = vm.$el.getElementsByTagName('h1')[0].innerHTML.trim();
    expect(domInner).to.be.equal(msg);
  })

  it('测试msg属性,能否正常渲染, vue test util', () => {
    const msg = 'hello world';
    // const wrapper = mount(HelloWorld, {
    //   propsData: {msg}
    // });
    const wrapper = mount(HelloWorld);
    wrapper.setProps({msg});
    const domInner = wrapper.find('h1').text();
    expect(domInner).to.be.include(msg);
  })
})
2)sinon

辅助我们进行前端测试。

安装:npm install sinon -D

引入: import sinon from ‘sinon’;

spy 间谍函数

const spy = sinon.spy();

  • 使用方法

    spy.called; 表示函数是否被调用,返回布尔值

    spy.callCount; 函数被调用的次数

    spy.calledOnce; 函数只被调用了一次,返回布尔值

    spy.calledTwice; 函数被连续调用了两次,返回布尔值

    spy.calledThrice; 函数被连续调用了三次,返回布尔值

    spy.firstCall; 函数第一次被调用。返回布尔值

    spy.secondCall; 函数第二次被调用。返回布尔值

    spy.thirdCall; 函数第三次被调用。返回布尔值

    spy.lastCall; 函数最后一次被调用。返回布尔值

    spy.calledOn(‘xxx’); 调用函数时,函数的this至少有一次是xxx,返回布尔值。

    spy.alwaysCalledOn(‘xxx’); 调用函数时,函数的this始终是xxx,返回布尔值。

    spy.calledWidth(1, 2, 3); 函数至少被调用一次,且参数包含1, 2, 3,返回布尔值。

    spy.calledOnceWith(1, 2, 3); 函数只被调用一次,且参数包含1, 2, 3,返回布尔值。

    spy.alwaysCalledWith(1, 2, 3); 函数被调用时传的参数始终包括1,2,3,返回布尔值。

    spy.calledWithExactly(1, 2, 3); 函数至少被调用一次,且参数只为1,2,3,返回布尔值。

    spy.alwaysCalledWithExactly(1, 2, 3); 函数被调用时传的参数始终只为1,2,3,返回布尔值。

    spy.calledWithNew(); 函数被作为构造函数new,返回布尔值。

    spy.neverCalledWith(1, 2, 3); 函数执行时,参数从不为1, 2, 3。返回布尔值。

    spy.threw(); 函数执行时,抛出一个异常。返回布尔值。

    spy.threw(“TypeError”); 函数执行时,至少抛出一次TypeError异常。返回布尔值。

    spy.threw(‘xxx’); 函数执行时,至少抛出一次xxx异常。返回布尔值。

    spy.alwaysThrew(); 函数执行时,始终抛出异常。返回布尔值。

    spy.alwaysThrew(“TypeError”); 函数执行时,始终抛出TypeError异常。返回布尔值。

    spy.alwaysThrew(‘xxx’); 函数执行时,失踪抛出xxx异常。返回布尔值。

    spy.returned(‘xxx’); 函数执行时,至少返回一次xxx。返回布尔值。

    spy.alwaysReturned(‘xxx’); 函数执行时,始终返回’xxx’。返回布尔值。

    spy.getCall(n); 返回函数被第n次调用。

    spy.getCalls(); 返回一个函数被调用的数组。

    spy.thisValues; 返回函数被调用时this指向的集合, 值类型为数组。

    spy.args; 返回函数被调用时参数,值类型为数组。

    spy.exceptions; 返回函数被调用时抛出的异常集合. 值类型为数组。

    spy.returnValues; 返回函数被调用时返回的值,值类型为数组。

3)jest
  • 匹配语法

    https://jestjs.io/docs/zh-Hans/using-matchers

  • mock函数

    https://jestjs.io/docs/zh-Hans/mock-functions

  • jest异步

    // axios.js文件
    
    export default {
      get: url => {
        switch (url) {
          case '/name':
            return Promise.resolve({
              status: 200,
              data: {
                name: 'shanshan'
              } 
            })
          case '/age':
            return Promise.resolve({
              status: 200,
              data: {
                age: 18
              } 
            })
        }
      }
    }
    
    // Async1.spec.js文件
    
    import Async from '@/components/Async';
    import { mount } from '@vue/test-utils';
    import Vue from 'vue';
    
    jest.mock('axios');
    
    describe('Async.vue', () => {
        
      // 利用Vue.nextTick解决异步问题。
      it('获取name', done => {
        const wrapper = mount(Async);
        wrapper.findAll('button').at(0).trigger('click');
        Vue.nextTick(() => {
          expect(wrapper.findAll('h4').at(0).text()).toBe('shanshan');
          done();
        })
      })
        
      // 利用Promise解决异步问题。
      it('获取name', () => {
        const wrapper = mount(Async);
        wrapper.findAll('button').at(0).trigger('click');
        return Promise.resolve().then(() => {
          expect(wrapper.findAll('h4').at(0).text()).toBe('shanshan');
        })
      })
        
      // 利用async、await解决异步问题。
      it('获取name', async () => {
        const wrapper = mount(Async);
        await wrapper.vm.getName();
        expect(wrapper.findAll('h4').at(0).text()).toBe('shanshan');    
      })
      
    })
    

四、element组件库

1、安装
  • vue add element 可选全部引入和按需引入
  • npm i element-ui -S 也可引入全部或按需引入
  • 完整引入
import Vue from 'vue';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import App from './App.vue';

Vue.use(ElementUI);

new Vue({
  el: '#app',
  render: h => h(App)
});
  • 按需引入

    import { Button, Select } from 'element-ui';
    Vue.component(Button.name, Button);
    Vue.component(Select.name, Select);
    /* 或写为
     * Vue.use(Button)
     * Vue.use(Select)
     */
    
2、修改element组件样式

k(() => {
expect(wrapper.findAll(‘h4’).at(0).text()).toBe(‘shanshan’);
done();
})
})

// 利用Promise解决异步问题。
it('获取name', () => {
  const wrapper = mount(Async);
  wrapper.findAll('button').at(0).trigger('click');
  return Promise.resolve().then(() => {
    expect(wrapper.findAll('h4').at(0).text()).toBe('shanshan');
  })
})
  
// 利用async、await解决异步问题。
it('获取name', async () => {
  const wrapper = mount(Async);
  await wrapper.vm.getName();
  expect(wrapper.findAll('h4').at(0).text()).toBe('shanshan');    
})

})


## 四、element组件库

#### 1、安装 

- `vue add element` 可选全部引入和按需引入
- `npm i element-ui -S`  也可引入全部或按需引入
- 完整引入

```js
import Vue from 'vue';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import App from './App.vue';

Vue.use(ElementUI);

new Vue({
el: '#app',
render: h => h(App)
});
  • 按需引入

    import { Button, Select } from 'element-ui';
    Vue.component(Button.name, Button);
    Vue.component(Select.name, Select);
    /* 或写为
     * Vue.use(Button)
     * Vue.use(Select)
     */
    
2、修改element组件样式

给组件加个class1,再找到组件样式对应的class2,写样式时在class2前面加个class1,这样的话能增加权重且不产生样式污染

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值