一、入门

<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

三大框架:

angular:模块化

react:虚拟DOM

vue:集大成者

axios:前端通信框架

jquery:dom操作频繁

解耦了

初体验

<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

或者:

<!-- 生产环境版本,优化了尺寸和速度 -->
<script src="https://cdn.jsdelivr.net/npm/vue"></script>

引入<script src="../js/vue.js"></script>

输出\{\{message}}

循环本标签:

<ul id="list">
    <li v-for="movies in movies">{{movies}}</li>
</ul>

跟踪

const 变量 let变量

<script>
const app = new Vue({
    el:'list',
    data:{
       movies:['s','d','b']
    }
})
</script>

事件

<button v-on:click="count++">+</button>

函数

<script>
const count = new Vue({
    el:"#count",
    data:{
        count:1
    },
    methods:{
        add:function(){
            this.count++;
        },
        sub:function(){
            this.count--;
        }
    }
})
</script>
使用:
<button v-on:click="add">
    +
</button>
<button @click="sub">
    -
</button>

MVVM

image-20200401001217340 image-20200401001244475 image-20200401001431145

属性

el、data、methods 、computed、filter、watch

  • el: string | htmlElement
  • data: Object | Function
  • computed
  • methods: { key:string : Function }
  • filter
  • watch

生命周期函数

beforeCreate、created、mounted、

Vue 实例生命周期

模板templates

缩进:2

vue + ctrl + e

属性

  • el属性

    • 用来指示vue编译器从什么地方开始解析 vue的语法,可以说是一个占位符。
  • data属性

    • 用来组织从view中抽象出来的属性,可以说将视图的数据抽象出来存放在data中。
  • template属性

    • 用来设置模板,会替换页面元素,包括占位符。
  • methods属性

    • 放置页面中的业务逻辑,js方法一般都放置在methods中
  • render属性

    • 创建真正的Virtual Dom
  • computed属性

    • 用来计算,有缓存
  • watch属性

    • watch:function(new,old){}
    • 监听data中数据的变化
    • 两个参数,一个返回新值,一个返回旧值,

双向数据绑定

MVVM,数据发生变化,视图也变化,视图变化,数据也同步变化

响应式

Object.keys(obj).forEach(key => {
    let value = obj[key];
    Object.defineProperty(obj, key, {
        set(newValue) {
            console.log("");
            value = newValue;
        },
        get() {
            return value
        }
    })
})

响应情况

数组

如push、splice、=赋值(array=[1,2,3])

无法监听

array[index] = 1
object.a = 3
array.length = 5

解决

// 这是个深度的修改,某些情况下可能导致你不希望的结果,因此最好还是慎用
this.$set(array, index, data);
  • 上面提到的splice方法进行增删改

  • 利用临时变量进行中转

let tempArr = [...this.targetArr]
tempArr[0] = {data: 'test'}
this.targetArr = tempArr

对象

对象的直接=赋值

this.obj = {name: 'test'}

无法监听到的

对象属性的增删改

obj: {
    prop1: 'data1',
    prop2: 'data2'
}
...
// 增
this.obj.prop3 = 'data3'
// 删
delete this.obj.prop1
// 改
this.obj.prop1 = 'data4'

解决

this.$set(obj, key ,value) ;//- 可实现增、改

// watch时添加deep:true深度监听,只能监听到属性值的变化,新增、删除属性无法监听
this.$watch('blog', this.getCatalog, {
    deep: true
    // immediate: true // 是否第一次触发
  });

// watch时直接监听某个key
watch: {
  'obj.name'(curVal, oldVal) {
    // TODO
  }
}

// object.assign()+直接=赋值
this.watchObj = Object.assign({}, this.watchObj, {
  name: 'xiaoyue',
  age: 15,
});

二、指令

常用指令:

  • v-if
  • v-else-if
  • v-else
  • v-for
  • v-on
  • v-model
  • v-bind

Mustache胡须

<div>
    {{  }} 、 \{{stirng1  string2}}
</div>

v-once

不会跟着改变

v-html

<h2 v-html="url">
    渲染出HTML标签,也会覆盖
</h2>
data:{
    url:"<a href='http://www.baidu.com'>d</a>"
}

v-text

<h2 v-text="message">
    会覆盖
</h2>

v-pre

不解析vue

斗篷

vue解析前不显示,vue解析后解释

<style>
    [v-cloak]{
        display:none;
    }
</style>
<div id="app" v-clock>
    {{message}}
</div>

v-bind

动态绑定值,URL等

动态绑定URL

<img v-bind:src="imgURL" alt="">
<a v-bind:href="aHref">百度</a>
简写:
<a :href="aHref">百度</a>

动态绑定class

1.

<div id="color">
    <h2 class="origin" :class="{active: isActive, line: isLine}">
        颜色1
    </h2>
    <h2 class="origin" :class="getClass()">
            颜色2
    </h2>
    <!-- v-on -->
    <button @click="butClick"> 
        变色
    </button>
</div>
<script>
   const color = new Vue({
            el:"#color",
            data: {
                isActive: true,
                isLine: true
            },
            methods: {
                butClick: function(){
                    this.isActive = !this.isActive;
                },
                getClass: function(){
                    return {active: this.isActive, line: this.isLine};
                }
            }
        })
</script>

2.

<h2 clsaa="title" :class="[active, line]"> 变量数组
    {{message}}
</h2>

动态绑定style

对象

<h2 :style="{fontSize:finalSize + 'px', backgroundColor: finalColor}"{{message}}</h2>
<script>
const app = new Vue({
    el: '#app',
    data: {
        message: 'hello',
        finalSize: 100,
        finalColor: 'red'
    },
    methods: {
        getStyles: function(){
            return {fontSize: this.finalSize + ‘px', backgroundColor: this.finnalColor}
        }
    }
})
</script>

数组

<div v-bind:style="[baseStyles, overridingStyles]">
    数组类型,存入 myColor: {color: 'red'} 对象即可
</div>

v-for

<div v-for="(movie, index) in movies" :key="movie">
 最好加上key,便于复用
 插入:插入位置后数组后移,性能低    
 有key,一一对应,虚拟dom对照,没变,性能高
</div>
<div v-for="(value, key, index) in class">

</div>
<script>
    var vm = new Vue({
        el: "#app",
        data: {
            item: [
                {message: "ming"},
                {message: "yue"}
            ]
        }
    })

</script>

实时更新

$set()方法重新渲染

this.$set(this.student,"age", 24)
Vue.set(this.arr, 0, 'asdf')
//this.student为你在data中声明的数组名,‘age’是你要改变的数组下的指定字段名,24是你要变化的值

v-on

事件绑定

参数

    <button v-on:click="btnClick()" >按钮</button>
1.如果原方法有参数,则给第一个参数传入event参数,原方法无参数,可以直接省略括号
    <button @click="btnClick">按钮</button>
    修改就调用
    <inpupt type="text" @input="fun()">

2.获取event参数:$event
    <button @click="btnClick('aaa', $event)">按钮</button>
3.修饰符
    .stop        多层触发
    .prevent    阻止默认事件
    .keyCode|.keyAlias    监听键盘点击 code或简写
        @keyup
        @keyup.enter
    .native        监听组件根元素的原生事件,组件点击
    .once        只触发一次回调

v-if

<h1 v-if="pl">
    yes
</h1>
<h1 v-else>
    no
</h1>
<h1 v-else-if="type===A">
    a
</h1>
<script>
    var vm = new Vue({
        el: "#app",
        data: {
            //ok: true
            type: 'A'
        }
    })
</script>
<div>
      登录切换
    虚拟dom,会出现input重用,输入框value不变
    不复用: 添加key属性,不同则不复用 
</div>

实例

test

<!DOCTYPE html>
<html>
<head>
    <title></title>
    <meta charset="UTF-8">
    <style type="text/css">
        .active{
            color:blue;
        }
    </style>
</head>

<body>
    输出:
    <div id="app">
        <h1>{{message}}</h1>
        <h2>{{name}}</h2>
        <h3 v-html="url">ddd</h3>
    </div>
    v-for:
    <ul id="list">
        <li v-for="movies in movies">{{movies}}</li>
    </ul>
    计数器:
    <div id="count">
        <h2>总数{{count}}</h2>
        <button v-on:click="add">+</button>
        <button @click="sub">-</button>
    </div>
    变色:
    <div id="color">
        <h2 :class="{active: isActive, line: isLine}">
            颜色
        </h2>
        <h2 class="origin" :class="getClass()">
            颜色2
        </h2>
        <!-- v-on -->
        <button @click="butClick"> 
            变色
        </button>
    </div>

    <script src="../js/vue.js"></script>
    <script>
        //let变量 const变量
        const app = new Vue({
             el:'#app',    //挂载元素
             data:{        //定义数据
                 message:'你好!vue!',
                 name:'vue',
                 url:"<a href='http://www.baidu.com'>d</a>"
             }
        })
        const list = new Vue({
            el:'#list',
            data:{
                movies:['s','d','b']
            }
        })

        const counter = new Vue({
            el:"#count",
            data:{
                count:1,
            },
            methods:{
                add:function(){
                    this.count++;
                },
                sub:function(){
                    this.count--;
                }
            }
        })

        const color = new Vue({
            el:"#color",
            data: {
                isActive: true,
                isLine: true
            },
            methods: {
                butClick: function(){
                    this.isActive = !this.isActive;
                },
                /*
                getClass: function(){
                    return {active: this.isActive, line: this.isLine};
                }*/
                getClass(){
                    return {active: this.isActive, line: this.isLine};
                }
            }
        })
    </script>
</body>
</html>

点击变色

<ul id="myTag">
        <li v-for="(movie,index) in movies" :class="getClass(index)"  @click="changeColor(index)">    
            {{index}}--{{movie}}
        </li>
    </ul>
        <script>
            const tag = new Vue({
                el: "#myTag",
                data: {
                    movies:["11", "22", "33", "44"],
                    isblue: [false, true, true, false],
                    isyellow: false
                },
                methods: {
                    changeColor: function(index){
                        this.$set(this.isblue, index, !(this.isblue[index]));
                    },
                    getClass: function(index){
                        return {blue: this.isblue[index], yellow: this.isyellow };
                    }
                }
            })
    </script>

v-model

忽略所有表单元素的value、checked、selected特性的初始值,总是将vue实例的数据作为数据来源

在ios中,用户无法选择第一个选项

v-model即可实现互斥,不需name,可以根据值默认选中

修饰符

  • lazy
    • 失去焦点或者回车才同步
  • number:
    • 数字类型
  • trim:
    • 去除空格

实现v-model

<div id="app">
    <input type="text" :value="message" @input="valueChange">
    <input type="text" :value="message" @input=" message = $event.target.value ">
    <h2>
        {{message}}
    </h2>
</div>
<script>
    const app = new Vue({
        el: "#app",
        data:{
            message: "你好"
        },
        methods: {
            valueChange(event){
                this.message = event.target.value;
            }
        }
    })
</script>

使用

UTOOLS1594026052800.png

1.text

<div id="app">
    <input  type="text" v-model="message" />
    {{message}}
</div>
<script>
    var data = new Vue({
        el: "#app",
        data: {
            message: "234"
        }
    })
</script>

2.radio

<div id="app">
    <input  type="radio" name="sex" value="" v-model="value" /><input  type="radio" name="sex" value="" v-model="value" />女
    选中了:{{value}}
</div>
<script>
    var data = new Vue({
        el: "#app",
        data: {
            value: ""
        }
    })
</script>

3.selected

<div v-model="selected">
    <seclet>
        <option value="" disabled>--请选择--</option>
        <option>A</option>
        <option>B</option>
        <option>C</option>
    </seclet>
    <span>value:{{selected}}</span>
</div>
<script>
    var vm = new Vue({
        el: "#app",
        data: {
            selected: ''
        }
    });
</script>

v-show

display

三、语法

计算属性

解析

存在缓存,比使用方法的性能高

将不经常变化的计算结果进行缓存,节省系统开销

例子:

{{compute}}
<script>
    computed:{
        fullName: function(){
            return this.name+ '  '+ this.yourname;
        }
    }
</script>

语法

<script>
computed:{
    fullName: {
        //一般不写,使用:app.fullName = "asdf asdf";
        set: function(newValues){
            console.log(newValue);
            const names = newValues.split(' ');
            this.name = name[0];
            this.yourname = name[1];
        },
        //一般简化
        get: function(){
            return this.name+ ' '+ this.yourname;
        }
    },
    // 简化
    getName(){

    }
}
 </script>

ES6

var、const、let

let存在缺陷

ES5之前: if与for没有块级作用域,共用循环i, for,使用闭包,函数存在作用域

ES5:闭包

for(var i = 0; i < btns.length; i++){
    (function(i){
        btn[i].addEventListener('click', function(){
            console.log("i");
        })
    })(i);
    /*函数中有自己的i,作用域
        (function(){})(i);
    */
}

ES6 : let / const let存在自己的作用域,const常量

const btns = documents.getElementsByTagName('button');
//如果是var,只会是5
for(let i = 0; i < btns.length ; i++){
    btns[i].addEventListener('click', function(){
        console.log("点击"+ i);
    })
}

const的对象不能修改,但是属性可以修改

对象增强写法

//1.属性增强方法
    let name = "myname";
    //直接赋值
    let obj = {
        name
    }

//2.方法增强
    let obj = {
        run(){
            console("ES6");
        }
    }
    //未增强
    let obj = {
        run: function(){

        }
    }

两空格

箭头函数

const sum = (num1, num2) => {
    return num1 + num2;
}
const power = num => {
    return num * num;
}
const mul = (num1, num2) => num1 + num2;

箭头函数中的this 引用最近作用域的this

UTOOLS1594270893413.png

UTOOLS1594270971727.png

字符串

str.startsWith("")
    .endWith("")
    .includes("");

字符串模板

var str = `${age}你好`

解构

var test = ( {name} ) => console.log("hello", name)
test( {name: "sdf"} );

对象优化

Object.keys(person) // array[]
Object.values(person) // arrays[]
Object.entries(person) /* [
                            [ "name", "mingyue" ], 
                        ]*/

Object.assign(origin, newObj1) // 赋值到origin中

数组响应式

下标修改无法响应

pop() 删除最后一个

shift() 删除第一个

unshift() 第一个添加

push() 最后一个添加

splice() start, [length|0, [‘a’, ‘b’, ‘c’] ]

reverse()

filter

// 全局
Vue.filters("showValue", function(val) {

})

// 局部
filters: {
    showValue(value){
        return "¥" + value.toFixed(2);
    }
}

{{sumValue | showValue}}

购物车

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <script src="./vue.js"></script>
</head>
<body>
    <div id = "table">
        <table border  v-if="books.length">
            <tr>
                <td>id</td>
                <td>书名</td>
                <td>价格</td>
                <td>数量</td>
                <td>操作</td>
            </tr>
            <tr v-for="(book,index) in books">
                <td>{{book.id}}</td>
                <td>{{book.name}}</td>
                <td>{{book.value | showValue}}</td>
                <td>{{book.num}}</td>
                <td>
                    <button @click="addBookNum(index)">添加</button>
                    <button @click="subBookNum(index)">减少</button>
                    <button @click="remBook(index)">移除</button>
                </td>
            </tr>
            <tr>
                <td colspan="5">合计:{{sumValue | showValue}}</td>
            </tr>
        </table>
        <h2 v-else>
            购物车为空
        </h2>
    </div>
    <script>
        var app = new Vue({
            el: "#table",
            data: {
                books:[
                    {
                        id: 1,
                        name: "《算法1》",
                        value: 85.10,
                        num: 1
                    },
                    {
                        id: 2,
                        name: "《算法2》",
                        value: 65,
                        num: 1
                    },
                    {
                        id: 3,
                        name: "《算法3》",
                        value: 95,
                        num: 1
                    }
                ]
            },
            computed: {
                sumValue(){
                    return this.books.reduce((preTotal, book) => {
                        return preTotal + book.value *book.num;
                    }, 0);
                    //let sum = 0;
                    //for(let index = 0; index < this.books.length; index++){
                    /*
                    for(let index in this.books){
                        let book = this.books[index];
                        sum += book.value * book.num;
                    }

                    for(let book of this.books){
                        sum += book.value * book.num;
                    }
                    return sum;
                    */
                }
            },
            methods: {
                remBook(index){
                    this.books.splice(index, 1);
                },
                subBookNum(index){
                    if(this.books[index].num > 1){
                        this.books[index].num--;
                    }
                    else{
                        this.remBook(index);
                    }
                },
                addBookNum(index){
                    this.books[index].num++;
                }
            },
            filters: {
                showValue(value){
                    return "¥" + value.toFixed(2);
                }
            }
        })
    </script>
</body>
</html>

获取配置值

process.env.BASE_API

h()渲染

h() 函数是一个用于创建 VNode 的实用程序

// @returns {VNode}
h(
    // {String | Object | Function} tag
    // 一个 HTML 标签名、一个组件、一个异步组件、或
    // 一个函数式组件。
    //
    // 必需的。
    'div',

    // {Object} props
    // 与 attribute、prop 和事件相对应的对象。
    // 这会在模板中用到。
    //
    // 可选的(在开发时。建议传,实在没有传的时候,传入 null)
    {},

    // {String | Array | Object} children
    // 子 VNodes, 使用 `h()` 构建,
    // 或使用字符串获取 "文本 VNode" 或者
    // 有插槽的对象。
    //
    // 可选的。
    [
        'Some text comes first.',
        h('h1', 'A headline'),
        h(MyComponent, {
            someProp: 'foobar'
        })
    ]
)

四、vue组件

组件

基础

可复用的Vue实例,JSTL的自定义标签

UTOOLS1594038128961.png

<div id="app">
    <mingming></mingming>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
    Vue.component("mingming",{
        template: '<li>hello</li>'
    }); 
    var vm = new Vue({
        el: "#app"
    });   
</script>

2.通过v-bind将item作为参数name的值传入props
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<div id="app">
    <mingming v-for="(item,index) in items" v-bind:name="item" :yindex="index"></mingming>
</div>
<script>
    Vue.component("mingming",{
        props: ['name', 'yindex'],
        template: '<li>{{yindex}}{{name}}</li>'
    }); 
    var vm = new Vue({
        el: "#app",
        data: {
            items: ["ming", "fang", "love"]
        }
    });   
</script>

script标签写html

<script type="text/x-template" id="cpn">
    <div>
        <h1>标题</h1>
    </div>
</script>

template: "#cpn"

template标签写html

<template id = "cpn">
    <div>
        <h1>标题</h1>
    </div>
</template>

数据通信

<script>
    Vue.component("mingming",{
        template: '<li>{{yindex}}{{name}}</li>',
        data(){
            return {
                title: 'mingyue'
            }
        }
    }); 
    var vm = new Vue({
        el: "#app",
        data: {
        }
    });   
</script>

slot

  • slot中定义name,使用替换时使用slot=name进行替换

  • 父中使用slot-scope接受子组件传递的值,子中使用:name=进行传递

基础

<div id="app">
    <cpn></cpn>
    <cpn><i>一对多,替换全部无name的slot</i></cpn>
</div>
<template id="cpn">
    <div>
        组件
    </div>
    <slot>
        <button>
            默认显示
        </button>
    </slot>
</template>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>

    <div id="app">
        <todo>
            <todo-title slot="todo-title" :title="title"></todo-title>
            <todo-items slot="todo-items" v-for="item in todoItems" :item="item"></todo-items>
        </todo>
    </div>
    <script type="text/javascript">
        Vue.component("todo", { 
            template: '<div>\
                            <slot name="todo-title"></slot>\
                            <ul>\
                                <slot name="todo-items"></slot>\
                            </ul>\
                       </div>'
        });
        Vue.component("todo-title", {
            props: ['title'],
            template: '<div>{{title}}</div>'
        });
        Vue.component("todo-items", {
            props: ['item'],
            template: '<li>{{item}}</li>'
        });
        var app = new Vue({
            el: "#app",
            data: {
                title: "mingming",
                todoItems: ["mingfu", "fangfang", "love"]
            }
        })
    </script>
</body>
</html>

slot作用域

<div id="app">
    <cpn>
        <template slot-scope="slot">
            使用组件中的值
            <span v-for="item in slot.data">{{item}} </span>
            <span>{{slot.data.join("-")}}</span>
        </template>
    </cpn>
</div>

<template id="cpn">
    <div>
        <slot :data="languages">
            将子组件中的language传到data中,便于父组件调用
            <ul>
               <li v-for="item in languages">{{item}}</li> 
            </ul>
        </slot>
    </div>
</template>

删除

this.$emit(functionName, params) 给父组件发送事件

v-on:newFunName="funName(param)"

splice(index, num)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>

    <div id="app">
        <todo>
            <todo-title slot="todo-title" :title="title"></todo-title>
            <todo-items slot="todo-items" v-for="(item, index) in todoItems" :item="item" :index="index" v-on:remove="removeItem(index)"></todo-items>
        </todo>
    </div>
    <script type="text/javascript">
        Vue.component("todo", {
            template: '<div>\
                            <slot name="todo-title"></slot>\
                            <ul>\
                                <slot name="todo-items"></slot>\
                            </ul>\
                       </div>'
        });
        Vue.component("todo-title", {
            props: ['title'],
            template: '<div>{{title}}</div>'
        });
        Vue.component("todo-items", {
            props: ['item', 'index'],
            template: '<li>{{index}}---{{item}}---<button v-on:click="remove(index)">删除</button></li>',
            methods: {
                remove: function(index){
                    this.$emit('remove' , index);
                }
            }

        });
        var app = new Vue({
            el: "#app",
            data: {
                title: "mingming",
                todoItems: ["mingfu", "fangfang", "love"]
            },
            methods: {
                removeItem: function(index){
                    alert("vue" +this.todoItems[index]);
                    this.todoItems.splice(index, 1);
                }
            }
        })
    </script>
</body>
</html>

props

给子组件传值



    const cpn = {
        template: "#cpn",
        // 1, 参数名称
        //props: ['item'],
        /* //2,限制类型
        props: {
            item: Array
        },
        */
        // 3, 默认值
        props: {
            item: {
                type: String,
                default: "none",
                required: true
            }
            // 数组,对象
            item: {
                type: Array,
                default(){
                    return [1,2,3]
                }
            }
        }

    }

this.$emit

父子通信

props、this.$emit(function, params)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <script src="./vue.js"></script>
</head>
<body>
    <div id = "first">
        <component1 :value1="value1"  @changevalue1="changeValue1"></component1>
        <div>
            父中的值{{value1}}
        </div>
    </div>
    <template id="component1">
        <div>
            输入框1:<input type="text" v-model="cvalue" @input="changeCValue">
            {{cvalue}}
        </div>
    </template>
    <script>
        var app = new Vue({
            el: "#first",
            data: {
                value1: 1
            },
            methods: {
                changeValue1(value1){
                    this.value1 = value1;
                }
            },
            components: {
                component1: {
                    template: "#component1",
                    props:{
                        value1: [Number, String]
                    },
                    data(){
                        return {
                            cvalue: this.value1
                        }
                    },
                    methods:{
                        changeCValue(event){
                            // 将触发父组件中的changevalue1事件,触发后使用changeValue1函数进行执行
                            this.$emit("changevalue1", this.cvalue);
                        }
                    }

                }
            }
        });
    </script>
</body>
</html>

父子访问

父访问子

  • this.$children[0] 获取数组第一个

  • $refs reference引用,对象

    • <component1 ref = "a"></component>
    • this.$ref.a

子访问父

this.$parent

访问root

this.$root

引入css

<style>
    @import "./assets/css/base.css";
</style>

Vue.nextTick

https://segmentfault.com/a/1190000012861862

响应式数据改变后无法立刻获取到dom,可以使用Vue.nextTick延迟获取

五、Axios

初见

开源的主要作用实现ajax异步通信

引入:

npm install axios --save

http://www.axios-js.com/

idea更改es6

jquery对Dom操作频繁

<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<div id="vue" v-cloak>
    {{info.name}}<br>
    <a :href="info.url">点我</a>
    {{info.items[0]}}
</div>
<script>
window.onload = function(){
    var vm = new Vue({
        el: "#vue",
        data(){
            return{
                info:{
                    name: null,
                    url: null,
                    items: null
                }
            }
        },
        mounted(){
            axios.get("./json/data.json").then(response => (this.info = response.data));
        } 
    });
    axios({
        method: "get",
        url: "./data.json",
        responseType:  'stream'
    }).then(function(response){
        console.log(response);
    });
}    
</script>

ajax

<script>
    $.ajax({
        type: "get",
        url: "./test.html"
        data: $("form").serialize(),
        datatype: "json",
        success: function(){

        }
    })
</script>

axios

import axios from 'axios'
Vue.prototype.$axios = axios;

vue.config.js

module.exports = {
    devServe: {
        open: true,
        host: 'localhost',
        port: 8080,
        https: false,
        hotOnly: false,
        proxy: {
            // 配置跨域
            '/api': {
                target: 'https://ele-interface.herokuapp.com/api/',
                ws: true,        //如果要代理 websockets,配置这个参数
                secure: false,  // 如果是https接口,需要配置这个参数
                changeOrigin: true,  //是否跨域
                pathRewrite:{
                    '^/api':''
                }
            }
        }    
    }
}

请求

请求方式

  • axios(config)
  • axios.request(config)
  • axios.get(url[, config])
  • axios.delete(url[, config])
  • axios.head(url[, config])
  • axios.post(url[, data[, config]])
  • axios.put(url[, data[, config]])
  • axios.patch(url[, data[, config]])
axios({
  //url: "http://123.207.32.32:8000/home/multidata",
  //url: "@/assets/data.json",
  url: "http://123.207.32.32:8000/home/data?type=sell&page=1",
  params: {
    type: "sell",
    page: 1
  },
  method: 'get'
}).then((data) => {
  console.log(data);
})

配置

  • url
  • method
  • baseURL
  • transformRequest:[function(data){}] 请求前数据处理
  • transformResponse: [function(data){}] 请求后的数据处理
  • headers: {} 自定义的请求头
  • params: {} url查询对象
  • paramsSerializer: function(params){} 查询对象序列化参数
  • data: {key: ‘aa’} request body
  • timeout: 100 超时设置
  • withCredentials: false, 跨域是否携带Token
  • adapter: function(resolve, reject, config){} 自定义请求处理
  • auth: {name: “ming”} 身份验证信息
  • responseType: ‘json’ 响应数据格式 json/blob/document/arraybuffer/text/stream

并发请求

axios.all([
    axios({

}), axios({

})])/*.then(results => { // 1. results[0]

})*/.then(axios.spread( (result1, result2) => { // 2. 将结果分离    

}))

全局配置

UTOOLS1594452107964.png

axios.defaults.baseURL = "";
axios.defaults.timeout = 5000;
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';

axios({
    url: "/home" //自动携带baseURL
})

分类配置

const instance1 = axios.create({
    baseURL: 'http://123.207.32.32:8080',
    timeout: 5000
});

instance1({
    url: '/home/multidata',
    params: {

    }
}).then(res => {
    console.log(res);
})

封装

避免更改框架,每个组件都引入同一个框架,修改麻烦,将框架进行封装,只需要改一个地方

request01.js

  • 可以直接传入回调函数,请求完成后执行回调函数

  • 可以使用promise使用resolve返回数据

import axios from 'axios';
export function request(config) {

    return new Promise((resolve, reject) => {
        const instance = axios.create({
            baseURL: 'http://123.207.32.32:8080',
            timeout: 5000
        })
        // 1. resove,多余
        instance(config)
            .then(res => {
                resovle(res);
            })
            .catch(err => {
                reject(err);
            })
    })

    // 2. instance(config)返回promise
    const instance = axios.create({
            baseURL: 'http://123.207.32.32:8080',
            timeout: 5000
        })
    return instance(config);
}

使用

request({
    url: ""
}).then(res => {

}).catch(err => {

})

拦截器

请求拦截

const instance = axios.create({
    baseURL: 'http://123.207.32.32:8080',
    timeout: 5000
})
instance.interceptors.request.use(config => {
    console.log(config); // 获取到传入的config
    // 1. 过滤config信息

    // 2. 请求图标

    return config; // 需要交还config
}, err => {
    console.log(err);
})

响应拦截

const instance = axios.create({
    baseURL: 'http://123.207.32.32:8080',
    timeout: 5000
})
instance.interceptors.response.use(res => {
    console.log(res);
    return res.data;  // 只返回data
}, err => {
    console.log(err);
})

跨域

同源:域名、协议、端口均相同

六、vue-cli

vue-cli官方提供的脚手架,快速生成一个vue项目模板。

command-line interface 命令行界面

预先定义好的目录结构以及基础代码

功能:

  • 同一目录结构
  • 本地调试
  • 热部署
  • 单元测试
  • 集成打包上线

npm

node -v

npm -v

淘宝镜像加速器

npm install cnpm -g

#npm更换淘宝镜像
npm config set registry https://registry.npm.taobao.org

vue-cli

cnpm install -g @vue/cli
vue --version
npm install @vue/cli-init -g    2.x版本
npm clean cache --force

cnpm install vue-cli -g
#是否成功
vue list

vue cli2
vue init webpack mingyue
npm run dev
npm run build

vue cli3
vue create my-project
npm run serve
vue ui   // 进行配置

UTOOLS1589945224281.png

UTOOLS1594224476544.png

ES-lint js规范

项目下

npm install

npm audit fix

npm run dev

dist

UTOOLS1594285224975.png

webpack

https://www.webpackjs.com

ES6打包成ES5

npm install webpack@3.6.0 -g   [ --save-dev 本地安装-开发时依赖]
webpack src/main.js dist/bundle.js  》全局
webpack ./src/main.js --output-filename ./dist/bundle.js --output-path . --mode development
cnpm

npm install webpack -g

npm install webpack-cli -g

webpack -v

webpack-cli -v

演变

模块化

解决变量冲突,代码不可复用(使用闭包解决变量冲突)

代码复用:

var module1 = (function(){
    var obj = {};
    obj.name = "ming";
    obj.sum = function(a, b){
        return a+b;
    }
    return obj;
})()
<script src=""></script>

commonJs

require("modle");
require("../module.js");
export.doStuff = function(){};
module.exports = someValue;

// 导出
module.exports = {
   flag, sum
}

// 导入
var {flag, sum} = require('./aaa.js');
require("./css.css"); // 需要loader
// npm install --save-dev css-loader

缺点:

同步加载,阻塞加载

AMD

define("module", ["dep1", "dep2"], function(d1, d2)){
       retrun someExportedValue;
       }

异步加载模块,并行加载多个模块

缺点:提高开发成本

CMD

SPM打包,模块加载逻辑偏重

ES6模块

编译时就能确定模块的依赖关系

import "jquery";
export function doStuff(){}
module "localModule" {}

// 导出
export var a = 100;
export {
    flag, sum
}
export class Person{
    constructor(name, age){
        this.name = name;
    }
    run(){

    }
}
// default重命名,只能有一个
export default var name = "ming";
import myName from "./sdf.js";

// 导入
import {flag} from "./sdf.js";
import * as all from "./sdf.js";
<script type="module" src=""></script>

优点

容易静态分析

面向未来的EcmaScript标准

缺点

原生浏览器端没有实现该标准

只有新版的Nodejs才支持

尝试

UTOOLS1589954084993.png

UTOOLS1589954103241.png

UTOOLS1589954126896.png

UTOOLS1589954115805.png

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <script src="dist/js/bundle.js"></script>
</body>
</html>

原理

–save-dev 开发时依赖

–save 开发、发布都依赖

基础

  • css-loader/style-loader
  • less-loader/less
  • url-loaer/file-loader
  • babel-loader
  • vue-loader
  • webpack-dev-server
  • webpack-merge
main.js
    const {add} = require('./math.js');
    console.log(add(1, 2));
math.js
    function add(num1, num2){
    return num1 + num2;
    }
    module.exports = {
        add
    }

npm init 初始化项目,生成package.json文件
    "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1",
        "build": "webpack"
      }

npm install webpack@3.6.0 --save-dev
npm install
webpackage.config.js
    <script>
        const path = require('path');
        module.exports = {
          entry: './src/main.js',
          output: {
            path: path.resolve(__dirname, 'dist'),
            filename: 'bundle.js',
            //publicPath: 'dist/'   // url自动添加
          },
          module: {
            rules: [
                    {
                        test: /\.css$/,
                        // 只负责加载,不负责解析生效,
                        // 添加dom,   从右到左
                        use: ['style-loader', 'css-loader']
                    },
                    {
                    test: /\.(png|jpg|gif)$/,
                    use: [
                      {
                        loader: 'url-loader',
                        options: {
                          limit: 8192,
                          name: "images/[name][hash:8].[ext]"
                        }
                      }
                    ]
                    },
                    {
                test: /\.(png|jpg|gif)$/,
                use: [
                  {
                    loader: 'file-loader',
                    options: {
                        name: "images/[name][hash:8].[ext]"
                    }
                  }
                ]
              },
              {
                  test: /\.js$/,
                  exclude: /(node_modules|bower_components)/,
                  use: {
                      loader: 'babel-loader',
                      options: {
                          presets: ['@babel/preset-env']
                      }
                  }
              },
              {
                  test: /\.vue$/,
                  use: ['vue-loader']
              }
            ]
          },
          resolve: {
              alias: {
                  'vue$': 'vue/dist/vue.esm.js' // 用 webpack 1 时需用 'vue/dist/vue.common.js'
              }
          }
        };
    </script>
npm run build即可运行

npm install --save-dev css-loader // 能导入css
npm install --save-dev style-loader // 解析css

url-loader
    jpg: 转化为base64     
npm install --save-dev url-loader

file-loader
npm install --save-dev file-loader

babel
npm install babel-loader@8.0.0-beta.0 @babel/core @babel/preset-env webpack

vue
npm install vue --save
import Vue from 'vue'    export default
    runtime-only 代码中不能有template
    runtime-compiler
resolve: {
    alias: {
      'vue$': 'vue/dist/vue.esm.js' // 用 webpack 1 时需用 'vue/dist/vue.common.js'
    }
  }

npm install vue-loader vue-template-compiler --save-dev
    import Vue from 'vue';
    import App from "./App.vue";
    new Vue({
          el: '#app',
          template: "<app></app>",
          components: {
            App
          } 
        });

    <template>
      <div id="example">{{message}}</div>
    </template>
    <script>
        export default {
          data(){
                return {
                    message: 'Hello Vue!'
                };
            }
        }
    </script>
    <style>
        #example{
            color: red;
            font-size: 50px;
        }
    </style>
"vue-loader": "^13.0.0",
npm install 更新
 {
    test: /\.vue$/,
    use: ['vue-loader']
}

html
npm install html-webpack-plugin --save-dev
const HtmlWebpackPlugin = require('html-webpack-plugin');

es6转化es5

babel

UTOOLS1594201295355.png

vue

npm install vue --save

尝试

npm、webpack、

UTOOLS1594201876576.png

import Vue from 'vue';
var app = {
    template: "<div>{{message}}</div>",
    data(){
        return {
            message: 'Hello Vue!'
      };
    }
}
new Vue({
      el: '#app',
      template: "<app></app>",
      components: {
          app
      } 
    });

plugin

版权

const webpack = require("webpack");
module.exports = {
  plugins: [
    new webpack.BannerPlugin("版权所有");
  ]
}

html

npm install html-webpack-plugin --save-dev  版本不对!
npm install html-webpack-plugin@3.2.0 --save-dev
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
  plugins: [
    new HtmlWebpackPlugin({
        template: 'index.html'
    })
  ]
}

压缩

npm install uglifyjs-webpack-plugin@1.1.1 --save-dev
const path = require('path');
const webpack = require("webpack");
const HtmlWebpackPlugin = require('html-webpack-plugin');
const UglifyjsWebpackPlugin = require('uglifyjs-webpack-plugin');
module.exports = {
  entry: './src/main.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js',
    //publicPath: 'dist/'   // url自动添加
  },
  module: {
    rules: [
            {
                test: /\.css$/,
                // 只负责加载,不负责解析生效,
                // 添加dom,   从右到左
                use: ['style-loader', 'css-loader']
            },
            {
            test: /\.(png|jpg|gif)$/,
            use: [
              {
                loader: 'url-loader',
                options: {
                  limit: 8192,
                  name: "images/[name][hash:8].[ext]"
                }
              }
            ]
            },
            {
        test: /\.(png|jpg|gif)$/,
        use: [
          {
            loader: 'file-loader',
            options: {
                name: "images/[name][hash:8].[ext]"
            }
          }
        ]
      },
      {
          test: /\.js$/,
          exclude: /(node_modules|bower_components)/,
          use: {
            loader: 'babel-loader',
            options: {
              presets: ['@babel/preset-env']
            }
          }
      },
      {
          test: /\.vue$/,
          use: ['vue-loader']
      }
    ]
  },
  resolve: {
    alias: {
      'vue$': 'vue/dist/vue.esm.js' // 用 webpack 1 时需用 'vue/dist/vue.common.js'
    }
  },
  plugins: [
    new webpack.BannerPlugin("版权所有"),
    new HtmlWebpackPlugin({
        template: 'index.html'
    }),
    new UglifyjsWebpackPlugin()
  ]
};

服务器

npm install --save-dev webpack-dev-server@2.9.1 

config
  devServer: {
      contentBase: './dist',
      inline: true
  }

package
"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack",
    "dev": "webpack-dev-server  --open"
  },

插件分离

npm install webpack-merge --save-dev
"webpack-merge": "^4.0.0"            版本问题

"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack --config ./build/prod.config.js", 
    "dev": "webpack-dev-server --config ./build/dev.config.js"
  }

dev.config.js

const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpackMerge = require('webpack-merge');
const baseConfig = require('./base.config')
module.exports = webpackMerge(baseConfig, {
    devServer: {
        contentBase: './dist',
        inline: true
    }
});

prod.config.js

const UglifyjsWebpackPlugin = require('uglifyjs-webpack-plugin');
const webpackMerge = require('webpack-merge');
const baseConfig = require('./base.config')
module.exports = webpackMerge(baseConfig, {
    plugins: [
      new UglifyjsWebpackPlugin()
    ]
})

路径

module.exports = {
  entry: './src/main.js',
  output: {
    path: path.resolve(__dirname, '../dist'),  // 更改
    filename: 'bundle.js',
    //publicPath: 'dist/'   // url自动添加
  }
}

render

差距

runtime+compile

UTOOLS1594256868328.png

runtime

UTOOLS1594256850624.png

new Vue({
    render: function(createElement){
        // 标签,属性,内容
        return createElement('h2', 
                             {class: 'box'}, 
                             ['hello world',createElement('button', ['按钮'])]);
    }
})

var myApp = {
  template: '<div>{{name}} myvue</div>',
  data(){
    return {
      name: 'hello!'
    }
  }
}
/* eslint-disable no-new */
new Vue({
  el: '#app',
  render: (c => c(myApp))
})

vue-cli3

配置

vue ui

vue.config.js

module.exports = {

}

七、vue-route

简介

后端渲染: 服务解析地址映射,使用jsp等

前端渲染:ajax发送请求,将数据渲染

spa页面:单页面 一次全部加载html + css +js :映射关系vue - router

location.hash = “aaa” 网页不进行刷新请求

history.pushState({}, ‘’, “home”) 栈

history.back() history.go(-1)

history.forward()

history.repalceState({}, ‘’, ‘about’) 无法返回

安装

npm install vue-router –save -dev

npm audit fix

routes添加路由规则,router实例中添加routers,将router加入Vue

new Vue({router}) <------ new VueRouter(router) <-------- routes

import VueRouter from 'vue-router'
import Vue from "vue";

Vue.use(VueRouter);
const routers = [

];

const router = new VueRouter({
    routers
})

export default router;

使用

App.vue

使用router-linkrouter-view

<template>
  <div id="app">
    <router-link to="/home">首页</router-link>
    <router-link to="/about">关于</router-link>
    <router-view></router-view>
  </div>
</template>

main.js

使用router

import Vue from 'vue'
import App from './App'
import router from './router';

Vue.config.productionTip = false


new Vue({
  el: '#app',
  router,
  render: (c => c(App))
})

router/index.js

注册路由

redirect: “/home”

mode: “history” 不使用hash

import VueRouter from "vue-router";
import Vue from "vue";
import Home from "../components/Home";
import About from "../components/About"

// 1.使用Vue.use(插件),安装插件
Vue.use(VueRouter);

const routes = [
    {
        path: '/',
        redirect: "/home"
    },
    {
        path: '/home',
        component: Home
    },
    {
        path: '/about',
        component: About
    }
];

const router = new VueRouter({
    routes,
    mode: "history",
    linkActiveClass: 'active'  // 修改活跃属性名
})

export default router;

实例

UTOOLS1589960060005.png

UTOOLS1589959859599.png

属性

  • tag 渲染成其他标签
  • replace 无法返回
  • active-class 修改活跃属性名

携带参数

<router-link :to="'/user/' + userId">将data中的userId替换</router-link> 
{
    path: '/user/:userId',
    component: user
}

获取参数

computed: {
    userId(){
        return this.$route.params.userId;  // router是大的,这个是route
    }
}

跳转

this.$router.push(‘/home’)

this.$router.replace()

懒加载

打包构建js会很大,一次加载完影响体验

使用到的组件再加载

const Home = () => import("../components/Home");
const About = () => import("../components/About");
const routes = [
    {
        path: '/',
        redirect: "/home"
    },
    {
        path: '/home',
        component: Home
    },
    {
        path: '/about',
        component: About
    }
];

const router = new VueRouter({
    routes,
    mode: "history"
})

export default router;

嵌套路由

定义路由添加children

const Home = () => import("../components/Home");
const About = () => import("../components/About");
const HomeNews = () => import("../components/News");
const HomeMessages = () => import("../components/Messages");
{
    path: '/home',
    component: Home,
    children: [
        {
            path: '',
            redirect: 'news'
        },
        {
            path: 'news',
            component: HomeNews
        },
        {
            path: 'messages',
            component: HomeMessages
        }
    ]
}

传值

params

localhost:8080/home/messages/234

携带参数

<router-link :to="'/user/' + userId">将data中的userId替换</router-link> 
{
    path: '/user/:userId',
    component: user
}

获取参数

computed: {
    userId(){
        return this.$route.params.userId;  // router是大的,这个是route
    }
}

querys

localhost:8080/home/messages?id=234

<router-link :to="{path: '/profile', query: {name: 'mingyue'}}">档案</router-link>

获取

this.$route.push({
    path: '/profile',
    query: {
        name: 'mingyue'
    }
})

继承

Vue.prototype.name = “mingyue”

所有组件都会有这个属性

更改title

created

created(){
   document.title("首页") ;
}

beforeEach

前置钩子

const routes = [
    {
        path: '/',
        redirect: "/home"
    },
    {
        path: '/home',
        component: Home,
        meta: {
            title: "首页"
        }
    }
];
router.beforeEach((to, from, next) => {
    document.title = to.matched[0].meta.title;
    console.log(to);
    next();
})

router.beforeEach((to, from, next) => {
  const isLogin = localStorage.ele_login ? true: false;
  if (to.path == '/login') {
    next();
  }else {
    isLogin? next(): next('/login');
  }

afterEach

后置钩子

router.afterEach((to, from) =>{
    console.log("---");
})

路由独享守卫

beforeEnter

const router = new VueRouter({
  routes: [
    {
      path: '/foo',
      component: Foo,
      beforeEnter: (to, from, next) => {
      }
    }
  ]
})

组件内守卫

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`
  }
}

keep-alive

属性函数

  • created
  • destroyed
  • activated 使用keep-alive有效
  • deactivated 使用keep-alive有效
  • beforeRouteLeave

keep-alive

属性

  • include 这些组件使用keep-alive
  • exclude 不使用
<keep-alive>
    <router-view></router-view>
</keep-alive>

<keep-alive exclude="About,Profile">
    <router-view></router-view>
</keep-alive>
export default {
  name: 'Home',
  data() {
    return {
      url: "/home/news"
    }
  },
  activated() {
    if(this.$route.path != this.url)
      this.$router.push(this.url);
  }
  ,
  beforeRouteLeave(to, from, next){
    this.url = this.$route.path;  // 通过该路由获取路由路径
    next();
  }
}

小案例:导航栏

使用@来定位到src目录

UTOOLS1594347819957.png

router/index.js

注册路由

import Vue from "vue";
import VueRouter from "vue-router";

Vue.use(VueRouter);

const Home = () => import("../views/home/Home.vue");
const Sort = () => import("../views/sort/Sort.vue");
const ShopCar = () => import("../views/shopcar/ShopCar.vue");
const Profile = () => import("../views/profile/Profile.vue");

const routes = [
    {
        path: "/",
        redirect: "/home"
    },
    {
        path: "/home",
        component: Home
    },
    {
        path: "/sort",
        component: Sort
    },
    {
        path: "/shopcar",
        component: ShopCar
    },
    {
        path: "/profile",
        component: Profile
    }

]

const router = new VueRouter({
    routes,
    mode: "history"
})

export default router;

main.js

使用路由

import Vue from 'vue'
import App from './App'
import router from './router';

Vue.config.productionTip = false

new Vue({
  el: '#app',
  router,
  render: h => h(App)
})

App.vue

使用组件、插入插槽、传递参数

<template>
  <div id="app" >
    <tar-bar>
      <tar-bar-item path="/home" color="blue">
        <img slot="item-icon" src="./assets/img/tabbar/home03.png" alt="">
        <img slot="item-icon-active" src="./assets/img/tabbar/home02.png" alt="">
        <div slot="item-text">首页</div>
      </tar-bar-item>
      <tar-bar-item path="/sort">
        <img slot="item-icon" src="./assets/img/tabbar/sort01.png" alt="">
        <img slot="item-icon-active" src="./assets/img/tabbar/sort02.png" alt="">
        <div slot="item-text">分类</div>
      </tar-bar-item>
      <tar-bar-item path="/shopcar">
        <img slot="item-icon" src="./assets/img/tabbar/shopcar01.png" alt="">
        <img slot="item-icon-active" src="./assets/img/tabbar/shopcar02.png" alt="">
        <div slot="item-text">购物车</div>
      </tar-bar-item>
      <tar-bar-item path="/profile">  
        <img slot="item-icon" src="./assets/img/tabbar/profile01.png" alt="">
        <img slot="item-icon-active" src="./assets/img/tabbar/profile02.png" alt="">
        <div slot="item-text">我的</div>
      </tar-bar-item>
    </tar-bar>
    <router-view></router-view>
  </div>
</template>

<script>
import router from "./router"
import TarBar from "./components/tabbar/TabBar.vue"
import TarBarItem from "./components/tabbar/TabBarItem.vue"

export default {
  name: 'App',
  router,
  components: {
    TarBar,
    TarBarItem
  }
}
</script>

<style>
  @import "./assets/css/base.css";
</style>

TabBar.vue

导航栏slot,准备好导航条样式

<template>
  <div id="tab-bar">
    <slot></slot>
  </div>
</template>

<script>
export default {
  name: 'TabBar',
  components: {
  }
}
</script>

<style>
  #tab-bar{
    display: flex;
    height: 49px;
    background: rgb(235, 231, 231);
    position: fixed;
    bottom: 0px;
    left: 0px;
    right: 0px;
  }
</style>

TabBarItem.vue

导航条中的各项,样式,准备好图片和文字的插槽,接收参数path和color,点击组件后this.$router.repalce(this.path)通过path,判断是否活跃this.$route.path.indexOf(this.path) !== -1

<template>
    <div class="tab-bar-item" @click="clickItem" >
      <div v-if="!isActive"><slot name="item-icon"></slot></div>
      <div v-else><slot name="item-icon-active"></slot></div>
      <div :style="textColor"><slot name="item-text"></slot></div>
    </div>
</template>
<script>


export default {
  name: 'TabBarItem',
  props: {
      path: String,
      color: {
        type: String,
        default: "red"
      }
  },
  components: {
  },
  data() {
    return {
    }
  },
  computed: {
    isActive(){
      return this.$route.path.indexOf(this.path) !== -1;
    },
    textColor(){
      return this.isActive? {color: this.color} : {};
    }
  },
  methods: {
    clickItem(){
      if(this.path != this.$route.path)
        this.$router.replace(this.path);
    }
  }
}
</script>

<style>
  .tab-bar-item{
    font-size: 13px;
    flex: 1;
    text-align: center;
    box-shadow: 0 -1px 3px rgba(235, 231, 231, 1);
    padding-top: 3px;
    cursor: pointer;
  }
  .tab-bar-item:hover{
    background-color: #ccc;
  }
  .tab-bar-item img{
    width: 29px;
    height: 29px;
    vertical-align: middle;  /* 能清除margin-bottom */
  } 
</style>

$router和$route

  • $router为 VueRouter 实例,是路由操作对象,只写对象,想要导航到不同url,则使用router.push方法

  • $route当前router跳转对象,路由信息对象,只读对象,里面可以获取name、path、query、params等

八、Element

网址

https://element.eleme.cn/#/zh-CN/component/installation

安装

npm i element-ui -S

CDN

<!-- 引入样式 -->
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
<!-- 引入组件库 -->
<script src="https://unpkg.com/element-ui/lib/index.js"></script>

开始

vue init webpack hello-vue
# 进入文件
# 安装 vue-router
npm install vue-router --save-dev

# element-ui
npm i element-ui -S

# 安装依赖
npm install

# 安装sass加载器
cnpm install sass-loader node-sass --save-dev

#启动
npm run dev

上手

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)
});
"sass-loader": "^8.0.2",

npm install

docsify文档

实例一

index.js

import Vue from 'vue'
import Router from 'vue-router'
import Main from '../views/Main'
import Login from '../views/Login'

Vue.use(Router);

export default new Router({
  routes:[
    {
      path: '/main',
      name: 'main',
      component: Main
    },
    {
      path: '/login',
      component: Login
    }
  ]
})

Login.vue

<template>
  <el-form :model="ruleForm" status-icon :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm">
    <el-form-item label="密码" prop="pass">
      <el-input type="password" v-model="ruleForm.pass" autocomplete="off"></el-input>
    </el-form-item>
    <el-form-item label="确认密码" prop="checkPass">
      <el-input type="password" v-model="ruleForm.checkPass" autocomplete="off"></el-input>
    </el-form-item>
    <el-form-item label="年龄" prop="age">
      <el-input v-model.number="ruleForm.age"></el-input>
    </el-form-item>
    <el-form-item>
      <el-button type="primary" @click="submitForm('ruleForm')">提交</el-button>
      <el-button @click="resetForm('ruleForm')">重置</el-button>
    </el-form-item>
  </el-form>
</template>


<script>
  export default {
    data() {
      var checkAge = (rule, value, callback) => {
        if (!value) {
          return callback(new Error('年龄不能为空'));
        }
        setTimeout(() => {
          if (!Number.isInteger(value)) {
            callback(new Error('请输入数字值'));
          } else {
            if (value < 18) {
              callback(new Error('必须年满18岁'));
            } else {
              callback();
            }
          }
        }, 1000);
      };
      var validatePass = (rule, value, callback) => {
        if (value === '') {
          callback(new Error('请输入密码'));
        } else {
          if (this.ruleForm.checkPass !== '') {
            this.$refs.ruleForm.validateField('checkPass');
          }
          callback();
        }
      };
      var validatePass2 = (rule, value, callback) => {
        if (value === '') {
          callback(new Error('请再次输入密码'));
        } else if (value !== this.ruleForm.pass) {
          callback(new Error('两次输入密码不一致!'));
        } else {
          callback();
        }
      };
      return {
        ruleForm: {
          pass: '',
          checkPass: '',
          age: ''
        },
        rules: {
          pass: [
            { validator: validatePass, trigger: 'blur' }
          ],
          checkPass: [
            { validator: validatePass2, trigger: 'blur' }
          ],
          age: [
            { validator: checkAge, trigger: 'blur' }
          ]
        }
      };
    },
    methods: {
      submitForm(formName) {
        this.$refs[formName].validate((valid) => {
          if (valid) {
            alert('submit!');
            this.$router.push("/main");
          } else {
            console.log('error submit!!');
            return false;
          }
        });
      },
      resetForm(formName) {
        this.$refs[formName].resetFields();
      }
    }
  }
</script>

Main.vue

<template>
    <h1>首页</h1>
</template>

<script>
    export default {
        name: "Main"
    }
</script>

<style scoped>

</style>

App.vue

<template>
  <div id="app">
    <h1>界面</h1>
    <router-link to="/main">main</router-link>
    <router-link to="/login">login</router-link>

    <router-view></router-view>
  </div>
</template>

<script>


export default {
  name: 'App',
  components: {

  }
}
</script>

<style>

</style>

index.js

import Vue from 'vue'
import Router from 'vue-router'
import Main from '../views/Main'
import Login from '../views/Login'

Vue.use(Router);

export default new Router({
  routes:[
    {
      path: '/main',
      name: 'main',
      component: Main
    },
    {
      path: '/login',
      component: Login
    }
  ]
})

实例二:导航栏

UTOOLS1589983145849.png

index.js

import Vue from 'vue'
import Router from 'vue-router'
import Main from '../views/Main'
import Login from '../views/Login'
import UserList from '../views/User/List'
import UserProfile from '../views/User/Profile'

Vue.use(Router);

export default new Router({
  routes:[
    {
      path: '/main',
      component: Main,
      children: [
        {
          path: '/user/profile',
          component: UserProfile
        },
        {
          path: '/user/list',
          component: UserList
        }
      ]
    },
    {
      path: '/login',
      component: Login
    }
  ]
})

main.vue

<template>
  <el-row class="tac">

    <el-col :span="12">
      <h5>自定义颜色</h5>
      <el-menu
        default-active="2"
        class="el-menu-vertical-demo"
        @open="handleOpen"
        @close="handleClose"
        background-color="#545c64"
        text-color="#fff"
        active-text-color="#ffd04b">
        <el-submenu index="1">
          <template slot="title">
            <i class="el-icon-location"></i>
            <span>导航一</span>
          </template>

          <el-menu-item-group>
            <span slot="title">分组一</span>
            <router-link to="/user/profile"><el-menu-item index="1-1">用户资料</el-menu-item></router-link>
            <router-link to="/user/list"> <el-menu-item index="1-2">用户列表</el-menu-item></router-link>
          </el-menu-item-group>
        </el-submenu>
      </el-menu>
    </el-col>
    <el-col :span="12">
      <router-view></router-view>
    </el-col>
  </el-row>
</template>
<script>
  export default {
    name: 'Main',
    methods: {
      handleOpen(key, keyPath) {
        console.log(key, keyPath);
      },
      handleClose(key, keyPath) {
        console.log(key, keyPath);
      }
    }
  }
</script>

传参

<script>
    export default new Router({
      routes:[
        {
          path: '/main',
          component: Main,
          children: [
            {
              path: '/user/profile/:id',    
              name: 'userProfile',    
              component: UserProfile //,props: true 解耦

            },
            {
              path: '/user/list',
              component: UserList
            }
          ]
        },
        {
          path: '/login',
          component: Login
        }
      ]
    })
</script>

<router-link :to="{name: 'userProfile',params: {id: 1}}"><el-menu-item index="1-1">用户资料</el-menu-item></router-link>

<h2>{{ $route.params.id }}</h2>

<!--
    props解耦  输出用 {{id}}
-->
<script>
    export default{
        props: ['id'],
        name: 'userProfile'
    }
</script>

重定向

<script>
export default new Router({
  routes:[
    {
        path: '/main',
        component: Main,
    }
    {
      path: '/goHome',
      redirect: '/main'
    }
  ]
})
</script>

登录

this.$router.push("/main/"+ this.ruleForm.pass);

模式

http://localhost:8080/main

不用#

<script>
export default new Router({
  mode: 'history',
  routes:[
    {
        path: '/main',
        component: Main,
    }
    {
      path: '/goHome',
      redirect: '/main'
    }
  ]
})
</script>

404

<template>
    <div><h1>404,页面不见了</h1></div>
</template>

<script>
    export default {
        name: "NotFound"
    }
</script>

<style scoped>

</style>

<script>
    import NotFound from '../views/NotFound'
export default new Router({
  mode: 'history',
  routes:[

    {
      path: '/main/:pass',
      component: Main,
      props: true,
    },
    {
      path: '/login',
      component: Login
    },
    {
      path: '*',
      component: NotFound
    }
  ]
})
</script>
<script>
export default new Router({
  mode: 'history',
  routes:[
    {
        path: '/main',
        component: Main,
    }
    {
      path: '/goHome',
      redirect: '/main'
    }
  ]
})
</script>
<script>
export default new Router({
  mode: 'history',
  routes:[
    {
        path: '/main',
        component: Main,
    }
    {
      path: '/goHome',
      redirect: '/main'
    }
  ]
})
</script>

钩子

  • to: 路由要跳转的路径信息
  • from:跳转前的路径信息
  • next:路由控制参数
    • next() 跳入下一个页面
    • next(‘/path’) 改变路由的跳转方向
    • next(false) 返回原来的页面
    • next((vm) => {}) 仅在beforeRouteEnter中可用,vm是组件实例
<script>
    export default {
        props: ['id'],
        name: "UserProfile",
        beforeRouteEnter: (to, from, next)=>{
          console.log("进入之前");
          next();
        },
        beforeRouteLeave: (to, from, next)=>{
          console.log("离开之前");
          next();
        }

    }
</script>

钩子中使用异步请求

安装

cnpm install --save axios vue-axios


<script>
    import Vue from 'vue'
    import axios from 'axios'
    import VueAxios from 'vue-axios'

    Vue.use(VueAxios, axios)
</script>
<script>
export default {
        props: ['id'],
        name: "UserProfile",
        beforeRouteEnter: (to, from, next)=>{
          console.log("进入之前");
          next(vm =>{
            vm.getData();//进入路由之前执行该方法
          });
        },
        beforeRouteLeave: (to, from, next)=>{
          console.log("离开之前");
          next();
        },
      methods: {
          getData: function(){
            this.axios({
              method: 'get',
              url: 'http://localhost:8080/static/mock/data.json',

            }).then(function(response){
              console.log(response);
            })
          }
      }

    }
</script>

九、Vuex

入门

Vuex是专为Vue.js应用程序开发的状态管理模式,集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式变化

组件同享变量,响应式

如:

  • 登录状态、用户名称、头像、地理位置

  • 商品收藏、购物车物品

devtools可以跟踪状态信息,但是异步不能用,使用actions

npm install vuex --save
import Vue from "vue";
import Vuex from "vuex";

Vue.use(Vuex);

const store = new Vuex.Store({
    state: {
        token: "abcdef",
        counter: 1
    },
    // 修改,方法中使用this.$store.commit('increase');
    mutations: {
        // 变化
        increase(state) {
            // 直接添加的属性不会响应式
            // Vue.set(state.info, 'address', '广西');
            // delete state.info.address 删除属性无法响应
            // Vue.delete(state.info, 'age'); 
            state.counter++;
        },
        // 用变量代替名字
        /*
        [INCREASE](state){

        }
        */
        decrease(state) {
            state.counter--;
        },
        increaseCount(state, counter) {
            state.counter = state.counter + counter;
        }
    },
    // 代替mutations,异步操作,必须通过mutations
    actions: {
        // 上下文, 参数
        aUpdateInfo(context, payload) {
            setTimeout(() => {
                context.commit('updateInfo'); // 调用mutation中的方法
            }, 1000)
        }
        // 使用this.$store.dispatch('aUpdateInfo', 'cannshu');
        // 1. 想知道修改完成? payload传入对象中有函数,commit之后运行函数即可
        // 2.使用new Promise((resolve, reject) => { resolve(""); })
        bUpdateInfo(context, payload) {
            return new Promise((resolve, reject) => {
                setTimeout(() => {
                    context.commit('updateInfo'); // 调用mutation中的方法
                    resolve("success");
                }, 1000)
            })
        }
        /*
          // 调用actions调用mutations,进行分配
          this.$store.dispatch("bUpdateInfo", 'infoaaa').then( (res) => {
              conosle.log(res);
          })
        */
    },
    // 对数据需要变化再拿,{{$store.state.powerCounter}}
    getters: {
        powerCounter(state) {
            return state.counter * state.statecount;
        },
        more20stu(state) {
            return state.students.filter( stu => stu.age > 20 )
        },
        // 使用已有的getters
        more20stuLength(state, getters) {
            return getters.more20stu.length; // 获取长度
        },
        // 传值
        moreAgeStu(state) {
            /*
            return function (age){
                return state.students.filter( stu => stu.age > age )
            }
            */
            return age => { return state.students.filter(  stu => stu.age > age  ); }
        },
        // 使用根模块的getters
        fullName(state, getters, rootGetters) {
            return getters.fullname2 + rootGetters.counter;
        }
        /*
            将方法放入vue实例中的计算属性
            import { mapGetters,mapAction } from 'vuex';
            computed:{
                ...mapGetters(['powerCounter']);
                ...mapAction(['gansha']);
                ...mapGetters({
                    powerCounter: 'powerCounter',
                    more20stu: 'more20stu'
                })
            }
        */
    },
    // 划分模块
    modules: {
        // 取出: $store.state.a
        a: {
            state: {},
            mutations: {}, // 与原来一样
            getters: {
                // 与原来一样
            }
        },
        b: moduleB
    }
})

export default store;

使用

{{$store.state.token}}
{{$store.getters.powerCounter}}
{{$store.getters.more20stu}}
{{$store.getters.moreAgeStu(20)}}

methods: {
    addition() {
        this.$store.commit('increase');
    },
    // @click="additionCounter(5)"
    additionCounter( count ) {
        this.$store.commit('increaseCounter', count)
        // 这将传入一整个对象
        this.$store.commit({
            type: 'incrementCount',
            count
        })
        // this.$store.dispatch('aUpdateInfo', 'canshu'); 异步操作时,使用actions调用mutation提交
    } 
}

文件抽取

UTOOLS1594447953455.png

事件总线

Vue.prototype.$bus = new Vue()
this.$bus.$emit()
this.$bus.$on("", ()  => { })

minxins

混入,可复用功能,引入后就可以直接用minxins的属性、方法

多个组件公用minxins,不会共享变量

created,mounted,会合并,先混和,再组件

十、前端基础

js

var、const、let

let存在缺陷

ES5之前: if与for没有块级作用域,共用循环i, for,使用闭包,函数存在作用域

ES5:闭包

for(var i = 0; i < btns.length; i++){
    (function(i){
        btn[i].addEventListener('click', function(){
            console.log("i");
        })
    })(i);
    /*函数中有自己的i,作用域
        (function(){})(i);
    */
}

ES6 : let / const let存在自己的作用域,const常量

const btns = documents.getElementsByTagName('button');
//如果是var,只会是5
for(let i = 0; i < btns.length ; i++){
    btns[i].addEventListener('click', function(){
        console.log("点击"+ i);
    })
}

const的对象不能修改,但是属性可以修改

对象增强写法

//1.属性增强方法
    let name = "myname";
    //直接赋值
    let obj = {
        name
    }

//2.方法增强
    let obj = {
        run(){
            console("ES6");
        }
    }
    //未增强
    let obj = {
        run: function(){

        }
    }

两空格

箭头函数

const sum = (num1, num2) => {
    return num1 + num2;
}
const power = num => {
    return num * num;
}
const mul = (num1, num2) => num1 + num2;

箭头函数中的this 引用最近作用域的this

UTOOLS1594270893413.png

UTOOLS1594270971727.png

字符串

str.startsWith("")
    .endWith("")
    .includes("");

字符串模板

var str = `${age}你好`

解构

var test = ( {name} ) => console.log("hello", name)
test( {name: "sdf"} );

对象优化

Object.keys(person) // array[]
Object.values(person) // arrays[]
Object.entries(person) /* [
                            [ "name", "mingyue" ], 
                        ]*/

Object.assign(origin, newObj1) // 赋值到origin中

promise

promise是异步编程的一种解决方案,如多层ajax嵌套

new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve("data");    //将数据传入下一步
        // 或者
        reject("error message")
    }, 1000)
}).then((data) => {
    // 正式进行处理

    // 1. 进行链式
    return new Promise((resolve) => {
        resolve("data1"); // 交给下一个处理
    })

    // 2. 简洁链式
    return Promise.resolve("data2"); 
    return Promise.reject()
    throw "error";

    // 3. 更简洁
    return "data2";

}).catch((err) => {
    // 错误处理
})
// then(success(), err())

async异步

sync同步

异步运行

// 两个异步运行,都获取结果才then
Promise.all([
    new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('result1');
        }, 2000)
    }),
    new Promise((resolve, reject)) => {
        setTimeout(() => {
            resolve('result2');
        }, 1000)
    }
]).then(results => {
    console.log(results);
})

实现

通过定义两个变量来保证都运行完成

UTOOLS1594373427231.png

数组filter

Array.filter(function(currentValue, indedx, arr), thisValue)

<script>
    let arr = [1,2,3,4,5,6,8];
    let newArr = arr.filter(function(arr){
        return arr < 3;
    });
    console.log(newArr);
</script>

数组map

map() 方法返回一个新数组,数组中的元素为原始数组元素调用函数处理后的值。

<script>
    let arr = [1,2,3,4,5,6,8];
    let newArr = arr.map(function(arr){
        return arr * 2;
    });
    console.log(newArr);
</script>

数组reduce

reduce() 方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值。

array.reduce(function(total, currentValue, currentIndex, arr), initialValue)

<script>
    let arr = [1,2,3,4,5,6,8];
    let sum = arr.reduce(function(preValue, value){
        return preValue + value;
    }, 0);// (函数, 初始值)
    console.log(sum);
</script>
<script>
let total = nums.filter(n => n < 100).map(n => n * 2).reduce( (pre, n) => pre + n);
</script>

对象解构

var obj = {
    name: "",
    age: 12
};
var {age, name} = obj;

push合并数组

arr.push(…ar2);

find - some - every - foreach

every()是对数组中每一项运行给定函数,如果该函数对每一项返回true,则返回true。

some()是对数组中每一项运行给定函数,如果该函数对任一项返回true,则返回true。

arr.find(item => item.flag);  //找到第一个
arr.foreach(item = > .......);
var arr = [ 1, 2, 3, 4, 5, 6 ]; 

console.log( arr.some( function( item, index, array ){ 
    console.log( 'item=' + item + ',index='+index+',array='+array ); 
    return item > 3; 
})); 

console.log( arr.every( function( item, index, array ){ 
    console.log( 'item=' + item + ',index='+index+',array='+array ); 
    return item > 3; 
}));

css

stick

超过则用fixed,常做导航栏

类类名选择器

li > [class^="local-nav-icon"]

before

.user::before {
    content: "";
    display: block;
    width: 23px;
    height: 23px;
    background: url(../img/sprite.png) no-repeat -59px -193px;
    background-size: 104px auto;
    margin: 0 auto;
    margin-top: 5px;
}

精灵图

.local-nav li [class^="local-nav-icon"] {
    margin-top: 5px;
    width: 40px;
    height: 40px;
    background-color: red;
    background: url(../img/sprite1.png) no-repeat 0px 0px;
    /* bottom center 底部中心对齐 */
    background-size: 40px auto;
}
.local-nav li .local-nav-icon-2 {
    background-position: 0 -40px;
}

overflow:hidden

用于border-radius

overflow-y: scroll;

可以部分显示,可滑动

:nth-child(-n+2)

前两个

:nth-child(n+2)

第二个开始

渐变

background: -webkit-linear-gradient(left,#fa6b54, #fa974d);

箭头

UTOOLS1594559361262.png

.sales_head a{
    position: absolute;
    top: 4px;
    right: 10px;
    padding: 3px 20px 3px 6px;
    border-radius: 18px;
    background:-webkit-linear-gradient(left, #ff506a, #ff6bc4);
    color: #fff;
}
.sales_head a:after{
    content: '';
    position: absolute; 
    top: 9px;
    right: 9px;
    width: 7px;
    height: 7px;
    border-top: 2px solid #fff;
    border-right: 2px solid #fff;
    transform: rotate(45deg);
}

省略号

UTOOLS1594618049430.png

不换行,省略号

.good-info p {
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
    margin-bottom: 3px;
}

vh

100 视口100

UTOOLS1594625362269.png

  .content {
    height: calc(100vh - 93px);  /* 空格! */
    overflow: hidden;
  }
  .content {
    position: absolute;
    top: 44px;
    bottom: 49px;
  } 

居中显示

.toast {
    position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%)
}

居中

left: 50%;
transform: translateX(-50%);

flex

开始

display: flex

父属性

  • flex-direcction 设置主轴方向
    • row 默认从左到右
    • row -reverse 右到左
    • column 上下
    • column-reverse
  • justify-content 主轴上子元素排列方式
    • flex-start 头部开始
    • flex-end 尾部开始
    • center 在主轴居中对齐
    • space-around 平分剩余空间
    • space-between 两边贴边,剩下平分
    • space-evenly 均等分
  • flex-wrap 子元素是否换行,默认不换行,将会平分
    • nowrap|wrap
  • align-content 设置侧轴子元素的排列方式,多行
    • flex-start 上到下
    • flex-end 下到上
    • center 居中
    • space-around 平分剩余空间
    • space-between 两边贴边,剩下平分
    • stretch 拉伸,无高度,则铺满
  • align-items 设置侧轴子元素的排列方式,单行
    • flex-start 上到下
    • flex-end 下到上
    • center 居中
    • stretch 拉伸,无高度,则铺满
  • flex-flow 同时设置flex-direction,flex-wrap
    • row | wrap

子属性

  • flex 占用的份数

    • 20% 父级的20%
    • 1
  • align-self

    • flex-end
  • order 排列顺序,数值小靠前,默认0

固定布局

html,body{
    width: 100%;
    height:100%;
}
* {
    padding: 0px;
    margin: 0px;
}
.main {
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    height: 100%;
}
.top, .bottom {
    height: 20px;
}
.center {
    flex: 1;
    overflow-y: auto;
}

十一、商城项目

初始化

https://github.com/coderwhy/supermall

目录

UTOOLS1594462255757.png

配置

vue.config.js

module.exports = {
    configureWebpack: {
        resolve: {
            alias: {
                'assets': '@/assets',
                'common': '@/common',
                'components': '@/components',
                'network': '@/network',
                'router': '@/assets'
            }
        }
    }
}

.editorconfig

root = true

[*]
charset = utf-8
indent_style = tab
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true

插件

npm install vue-router –save

先routes再router new VueRouter({routes})

注意路径问题

better-scroll

移动端下拉

https://github.com/ustbhuangyi/better-scroll/tree/v1.15.0

Iscroll

npm install better-scroll --save

npm install @better-scroll/core@next --save

import BScroll from 'better-scroll'
const scroll = new BScroll('.wrapper')

import BScroll from '@better-scroll/core'

wrapper需要有高度,其中只能有一个标签!

// 默认不实时检测,probeType: 2手指滑动过程检测,3滚动则检测
// click:true  按钮默认生效,div默认不生效
const bScroll = new BScroll(document.querySelector('wrapper'), {
    probeType: 2,  // 必须
    click: true,
    pullUpLoad: true    // 拉到底部

})
bScroll.on('scroll', (position) => {
    console.log(position);
})

bScroll.on('pullingUp', () => {
    console.log('ok');
    // 能继续拉
    bScroll.finshPullUp()
})
scrollTo(0, 0, 4000)

:probe-type="3"  当做数字传入

出现bug,无法显示

原因: 图片没加载完,没高度,scrollheight未更新,better-scroll无法下拉

解决:

  • this.$refs.scroll.scroll.refresh() 刷新高度
  • 每加载完一张图片就更新 img.onload = function(){}
    • @load发送到总线
Vue.prototype.$bus = new Vue()
this.$bus.$emit()
this.$bus.$on("", ()  => { })

防抖/节流

debounce/throttle

setTimeout没时间放到最后执行

// Home.vue
const refresh = this.debounce(this.$refs.scroll.refresh, 50);
this.$bus.$on("imgLoaded", () => {
    refresh();
});


// 避免频繁调用
debounce(func, delay) {
    let timer = null;    // 相当于全局
    return function(...args) {
        if(timer) {
            clearTimeout(timer);
        }
        timer = setTimeout(() => {
            func.apply(this, args);
        }, delay)
    }
}

吸顶灯

$el 获取组件的节点,offsetTop获取距离顶部高度

this.$refs.tabControll.$el.offsetTop;

切换原数据

keep-alive

UTOOLS1594784021993.png

数据完整

v-if="Object.keys(good).length ==0"

封装toast

index.js

import Toast from './Toast';
const obj = {};
obj.install = function(Vue) {
    // 1. 创建组件构造器
    const toastContrustor = Vue.extend(Toast);
    // 2. new一个组件
    const toast = new  toastContrustor();
    // 3. 将组件挂载上元素
    toast.$mount(document.createElement('div'))
    // 4. toast.$el对应的就是div
    document.body.appendChild(toast.$el);
    Vue.prototype.$toast = Toast;
}

Toast.vue

import Toast from './Toast';

const obj = {};
obj.install = function(Vue) {
    console.log("jinru")
    // 1. 创建组件构造器
    const toastContrustor = Vue.extend(Toast);
    // 2. new一个组件
    const toast = new  toastContrustor();
    // 3. 将组件挂载上元素
    toast.$mount(document.createElement('div'))
    // 4. toast.$el对应的就是div
    document.body.appendChild(toast.$el);

    Vue.prototype.$toast = toast;
    console.log("chuqu")
};
export default obj;

main.js

import toast from "../"
Vue.use(toast);

fastclick

解决移动端300ms延迟

npm install fastclick

import FastClick from 'fastclick';
FastClick.attach(document.body);

vue-lazyload

https://github.com/hilongjw/vue-lazyload

npm i vue-lazyload -S   //--save

main.js

import Vue from 'vue'
import App from './App.vue'
import VueLazyload from 'vue-lazyload'

Vue.use(VueLazyload)

// or with options
Vue.use(VueLazyload, {
  preLoad: 1.3,
  error: 'dist/error.png',
  loading: 'dist/loading.gif',
  // loading: require('./assets/img/common/placeholder.png')
  attempt: 1
})

new Vue({
  el: 'body',
  components: {
    App
  }
})

template

<ul>
  <li v-for="img in list">
    <img v-lazy="img.src" >
  </li>
</ul>

十二、饿了么项目

初始化

npm install -g @vue/cli

npm Install -g cnpm --registry=https://registry..npm.taobao.org

npm create eleme

npm run serve

reset.css

https://meyerweb.com/eric/tools/css/reset/

路由, 登录守卫

十三、工作总结

【app.xxx.js】存放的是项目中所有界面的业务逻辑代码,所以我们通常所说的异步组件就是指app.xxx.js文件,

  • index有时候只是引入js的html

  • app里放的只是公共apiurl,会有 文件0.xxx,1.xxx,不确定是哪个,打断点也走不到

  • 本次查找在文件1找到的

【manifest.xxx.js】可以理解为webpack打包时生成的配置文件,无需过多的关注里面的代码;
【vendor.xxx.js】存放的是各个组件、界面公用的一些代码,即第三方的库或者公共的基础组件