B站 - Vue 学习笔记

0 课程介绍


  • 邂逅Vue.js
  • Vue根底语法
  • 组件化开发
  • Vue CLI详解
  • vue-router
  • vuex详解
  • 网络封装
  • 我的项目实战

1 邂逅Vue.js


Vue (读音 /vju/,相似于 view) 是一套用于构建用户界面的渐进式框架

1.1 MVVM架构

  • MVVM架构

    • MVC与MVP

    • MVVM

  • Vue.js的定位

    • Vue.js在MVVM架构中的定位

    • Vue.js的功能定位

  • Vue.js的核心思想与特点

    • Reactive Components for Modern Web Interfaces(数据驱动的组件,为古代的 Web 界面而生)

      • 数据驱动
      • 组件化
    • 特点:侵入性低、激励模块化、轻量、高性能
  • 数据驱动的 Web 开发

    • Vue.js 装置

      • 下载Vue.js,通过<script>标签引入
      • CDN

        <!-- 开发环境版本,蕴含了有帮忙的命令行正告 --> <script class="lazy" referrerpolicy="no-referrer" data-src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script><!-- 生产环境版本,优化了尺寸和速度 --><script src="https://cdn.jsdelivr.net/npm/vue"></script>
      • NPM装置
      • vue-cli:配套路由等灵便的命令行框架
    • 传统视图驱动 Web 开发:通过原生js/jquery操作DOM元素

    • 数据驱动的 Web 开发:将Vue实例中数据和办法等,通过Vue指令的形式绑定到html中

1.2 Vue初体验

  • HelloVue.html

    <!DOCTYPE html><html lang="en">    <head>        <meta charset="UTF-8">        <title>TestVue</title>    </head>    <body>        <div id="app">{{message}}</div>        <script class="lazy" referrerpolicy="no-referrer" data-src="../js/vue.js"></script>        <script>            // 一、一般js写法(编程范式:命令式编程)            //1. 创立div元素,设置id属性            //2. 定义一个变量叫message            //3. 将message变量放在后面的div元素中显示            //4. 批改message数据            //5. 将message数据替换原数据            // 二、Vue写法(编程范式:申明式编程)            // let变量、const常量            // new Vue()就是ViewModel            const app = new Vue({                //el、data属于Vue的options                el: '#app', //挂载治理的元素                data: {  // 定义数据                    message: 'zfcer'                }            })            // Vue能够间接在浏览器中批改message:app.message = 'hello'        </script>    </body></html>
  • Vue列表展现

    <!DOCTYPE html><html lang="en">    <head>        <meta charset="UTF-8">        <title>Vue列表展现</title>    </head>    <body>        <!-- Vue 申明式 + 响应式 -->        <!-- 能够在浏览器中间接app.movies.push('d'),向数组movies中增加元素d -->        <div id="app">            <ul>                <li v-for="item in movies">{{item}}</li>            </ul>        </div>        <script src="../js/vue.js"></script>        <script>            const app = new Vue({                el: '#app',                data:{                    movies: ['a', 'b', 'c']                }            })        </script>    </body></html>
  • Vue案例-计数器

    <!DOCTYPE html><html lang="en">    <head>        <meta charset="UTF-8">        <meta http-equiv="X-UA-Compatible" content="IE=edge">        <meta name="viewport" content="width=device-width, initial-scale=1.0">        <title>计数器</title>    </head>    <body>        <div id="app">            <h1>以后计数:{{counter}}</h1>                        <!-- <button v-on:click="counter++">+</button><button v-on:click="counter--">-</button> -->            <button @click="add">+</button>            <button @click="sub">-</button>        </div>        <script src="../js/vue.js"></script>        <script>            const app = new Vue({                el: '#app',                data: {                    counter: 0                },                methods: {                    add: function(){                        console.log("add执行了")                        // 要用this.                        this.counter++;                    },                    sub: function(){                        this.counter--;                    }                }            })        </script>    </body></html>

1.3 Vue生命周期

在github中下载vue源码(tag2.6.14)后,用vscode关上src --> core --> index.js,发现import Vue from './instance/index'

关上./instance/index后就能够看到Vue函数,==options示意new Vue({})传入的el、data、methods==

import { initMixin } from './init'function Vue (options) {  if (process.env.NODE_ENV !== 'production' &&    !(this instanceof Vue)  ) {    warn('Vue is a constructor and should be called with the `new` keyword')  }  this._init(options)}

进入./init后,发现生命周期

// expose real selfvm._self = vminitLifecycle(vm)initEvents(vm)initRender(vm)callHook(vm, 'beforeCreate')initInjections(vm) // resolve injections before data/propsinitState(vm)initProvide(vm) // resolve provide after data/propscallHook(vm, 'created')
  • Vue官网生命周期流程

  • 了解Vue生命周期

2 Vue根底语法


  • vscode前端代码标准缩进设置为2

2.1 Mustache语法

  • Mustache中能够写简略的表达式,如果是简单的表达式举荐应用computed计算属性。==Mustache也是响应式的==。Mustache作用在显示内容自身。
<div id="app">    <h2>{{message}}</h2>    <!-- Mystache语法实用于标签内容上,不应用与标签属性 -->    <!-- Mustache语法中,不仅仅能够间接写变量,也能够写简略的表达式 -->    <h2>{{firstName + lastName}}</h2>    <h2>{{firstName + ' ' + lastName}}</h2>    <h2>{{firstName}} {{lastName}}</h2>    <h2>{{counter*2}}</h2></div><script class="lazy" referrerpolicy="no-referrer" data-src="../js/vue.js"></script><script>    const app = new Vue({        el: "#app",        data: {            firstName: "zfcer",            lastName: "520",            counter: 1,        },    });</script>

2.2 v-once语法

  • ==v-once是非响应式的==,不心愿随便更改数据或界面。v-once作用在标签上。
<div id="app">    <h2>{{message}}</h2>    <!-- 应用v-once后,上面的h2标签不会随着message扭转而扭转 -- 非响应式 -->    <!-- v-once间接应用 -->    <h2 v-once>{{message}}</h2></div><script src="../js/vue.js"></script><script>    const app = new Vue({        el: "#app",        data: {            message: "zfcer",        },    });</script>

2.3 v-html语法

  • v-html用来依照html格局进行解析显示,v-html作用在标签上。
  • 历史遗留问题: {{{}}}以前能够应用三大括号来解决html格局解析,当初不再反对了。
<div id="app">    <!-- 以前能够应用{{{}}}来代替,然而因为跟{{}}容易弄混,当初应用v-html -->    <!-- v-html前面跟url,作用在标签上 -->    <h2 v-html="url"></h2></div><script src="../js/vue.js"></script><script>    const app = new Vue({        el: "#app",        data: {            url: "<a href='http://ww.baidu.com'>百度一下</a>",        },    });</script>

2.4 v-text语法

  • v-text与Mustache比拟类似,然而v-text作用在标签上,并且会笼罩掉原来的内容。==不举荐应用==
<div id="app">    <h2>{{message}}, zfcer</h2>    <!-- 应用v-text会用message笼罩掉‘,zfcer’ -->    <!-- 不举荐应用 -->    <h2 v-text="message">, zfcer</h2></div><script src="../js/vue.js"></script><script>    const app = new Vue({        el: "#app",        data: {            message: "hello Vue",        },    });</script>

2.5 v-pre语法

  • v-pre用于跳过这个元素和它子元素的编译过程,用于==显示本来的Mustache语法==。
<div id="app">    <h2>{{message}}</h2>    <!-- 应用v-pre最开始编译过程中会显示{{message}} -->    <h2 v-pre>{{message}}</h2></div><script src="../js/vue.js"></script><script>    const app = new Vue({        el: "#app",        data: {            message: "hello Vue",        },    });</script>

2.6 v-cloak语法

  • 当Vue加载前,v-cloak存在;当Vue加载后,v-cloak被移除。
<!DOCTYPE html><html lang="en">    <head>        <meta charset="UTF-8" />        <title>v-cloak语法</title>        <style>            /* 暗藏元素信息{{message}} */            [v-cloak] {                display: none;            }        </style>    </head>    <body>        <div id="app" v-cloak>{{message}}</div>        <script src="../js/vue.js"></script>        <script>            // 当Vue加载前,v-cloak存在            // 当Vue加载后,v-cloak被移除            setTimeout(                // 延时加载                function () {                    const app = new Vue({                        el: "#app",                        data: {                            message: "hello Vue",                        },                    });                },                2000            );        </script>    </body></html>

2.7 v-bind语法

  • v-bind根本用法:==作用于标签属性上==,语法糖:
<div id="app">    <!-- Mustache语法不适用于标签属性上 -->    <a href="{{url}}">百度一下</a>    <br />    <!-- v-bind作用与标签属性上 -->    <a v-bind:href="url">百度一下</a>    <!-- v-bind语法糖: -->    <a :href="url">百度一下</a></div><script src="../js/vue.js"></script><script>    const app = new Vue({        el: "#app",        data: {            url: "http://www.baidu.com",        },    });</script>
  • v-bind动静绑定class对象:通过v-bind:class绑定标签上的class属性

    • 在v-bind:class属性中传入对象{style1: boolean1, style2: boolean2},能够做到==主动依据boolean来进行拼接==
    • 在一般class上定义公共style,最终会实现将一般class与v-bind:class进行拼接
    • v-bind:class也能够通过函数的形式传入对象
<!DOCTYPE html><html lang="en">    <head>        <meta charset="UTF-8" />        <title>v-bind动静绑定class(对象语法)</title>        <style>            .active {                color: red;            }            .line {                padding: 100px;            }        </style>    </head>    <body>        <div id="app">            <!-- 用一般class定义固定款式,v-bind绑定的class定义动静款式对象(会依据boolean值主动抉择对应的款式) -->            <!-- 最终会将一般class与v-bind绑定的class拼接起来 -->            <h2 class="title" :class="{active: isActive, line: isLine}">                Hello Vue (对象)            </h2>            <!-- 补充: 绑定函数 -->            <h2 class="title" :class="getClasses()">Hello Vue (对象)</h2>            <button @click="btnClick">点击变色</button>        </div>        <script src="../js/vue.js"></script>        <script>            const app = new Vue({                el: "#app",                data: {                    isActive: true,                    isLine: true,                },                methods: {                    btnClick: function () {                        this.isActive = !this.isActive;                    },                    getClasses: function () {                        return { active: this.isActive, line: this.isLine };                    },                },            });        </script>    </body></html>
  • v-bind绑定class数组:与v-bind绑定class对象相似
<!DOCTYPE html><html lang="en">    <head>        <meta charset="UTF-8" />        <title>v-bind动静绑定class(数组语法) - 不举荐</title>        <style>            .active {                color: red;            }            .line {                padding: 100px;            }        </style>    </head>    <body>        <div id="app">            <!-- 用一般class定义固定款式,v-bind绑定的class定义动静款式数组(会依据boolean值主动抉择对应的款式) -->            <!-- 最终会将一般class与v-bind绑定的class拼接起来 -->            <!-- 不举荐 -->            <h2 class="title" :class="[active, line]">Hello Vue (数组)</h2>            <!-- 补充: 绑定函数 -->            <h2 class="title" :class="getClasses()">Hello Vue (数组)</h2>            <button @click="btnClick">点击变色</button>        </div>        <script src="../js/vue.js"></script>        <script>            const app = new Vue({                el: "#app",                data: {                    active: "active",                    line: "line",                },                methods: {                    btnClick: function () {                        this.isActive = !this.isActive;                    },                    getClasses: function () {                        return [this.active, this.line];                    },                },            });        </script>    </body></html>
  • 练习:v-for与v-bind联合
<!DOCTYPE html><html lang="en">    <head>        <meta charset="UTF-8" />        <title>作业 - v-for和v-bind</title>        <style>            .active {                color: red;            }        </style>    </head>    <body>        <div id="app">            <!-- 计划一 -->            <ul>                <li :class="{active: isActive[index]}" v-for="(m, index) in movies">                    {{index}} - {{m}} <button @click="btnClick(index)">点击</button>                </li>            </ul>            <br>            <br>            <br>            <!-- 计划二:举荐 -->            <ul>                <li :class="{active: index == curIdx }" v-for="(m, index) in movies" @click="liClick(index)">                    {{index}} - {{m}}                 </li>            </ul>        </div>        <script src="../js/vue.js"></script>        <script>            const app = new Vue({                el: "#app",                data: {                    movies: ["aaa", "bbb", "ccc", "ddd"],                    isActive: [true, false, false, false],                    preIdx: 0,                    curIdx: 0                },                methods: {                    btnClick: function (index) {                        console.log("点击了" + index);                        this.isActive.splice(this.preIdx, 1, false)                        this.preIdx = index;                        Vue.set(this.isActive, index, true)                    },                    liClick(index){                        this.curIdx = index                    }                },            });        </script>    </body></html>
  • v-bind动静绑定style对象:应用驼峰命名
<!DOCTYPE html><html lang="en">  <head>    <meta charset="UTF-8" />    <title>v-bind动静绑定style(对象语法)</title>  </head>  <body>    <div id="app">      <!-- 应用驼峰命名:fontSize -- font-size -->      <h2 :style="{fontSize: finalSize+'px', color: finalColor}">        Hello Vue (对象)      </h2>      <!-- 补充: 绑定函数 -->      <h2 class="title" :style="getStyles()">Hello Vue (对象)</h2>    </div>    <script src="../js/vue.js"></script>    <script>      const app = new Vue({        el: "#app",        data: {          finalSize: 100,          finalColor: "red",        },        methods: {          getStyles: function () {            return {              fontSize: this.finalSize + "px",              color: this.finalColor,            };          },        },      });    </script>  </body></html>
  • v-bind动静绑定style数组
<!DOCTYPE html><html lang="en">    <head>        <meta charset="UTF-8" />        <title>v-bind动静绑定style(数组语法)</title>    </head>    <body>        <div id="app">            <!-- 应用驼峰命名:fontSize -- font-size -->            <h2 :style="[baseStyle, bgcStyle]">Hello Vue (对象)</h2>            <!-- 补充: 绑定函数 -->            <h2 class="title" :style="getStyles()">Hello Vue (对象)</h2>        </div>        <script src="../js/vue.js"></script>        <script>            const app = new Vue({                el: "#app",                data: {                    baseStyle: { fontSize: "100px", color: "red" },                    bgcStyle: { backgroundColor: "blue" },                },                methods: {                    getStyles: function () {                        return [this.baseStyle, this.bgcStyle];                    },                },            });        </script>    </body></html>

2.8 computed属性

  • computed根本语法
<div id="app">    <!-- 间接拼接,过于繁琐 -->    <h2>{{firstName +' '+ lastName}}</h2>    <h2>{{firstName}} {{lastName}}</h2>    <!-- Mustache语法中反对解析办法 -->    <h2>{{getFullName()}}</h2>    <!-- computed计算属性 -->    <h2>{{fullName}}</h2></div><script src="../js/vue.js"></script><script>    const app = new Vue({        el: "#app",        data: {            firstName: "zfcer",            lastName: "best",        },        // 计算属性:在变量没有批改状况下,第一次执行后有缓存        computed: {            fullName: function () {                return this.firstName + " " + this.lastName;            },        },        // 办法options:每次执行都要调用办法        methods: {            getFullName: function () {                return this.firstName + " " + this.lastName;            },        },    });</script>
  • computed简单操作
<div id="app">总价格为:{{totalPrice}}</div><script src="../js/vue.js"></script><script>    const app = new Vue({        el: "#app",        data: {            books: [                { id: 100, name: "aaa", price: 119 },                { id: 101, name: "bbb", price: 120 },                { id: 102, name: "ccc", price: 101 },                { id: 103, name: "ddd", price: 99 },            ],        },        computed: {            totalPrice: function () {                let finalPrices = 0;                // for(let i=0; i<this.books.length; i++){                //   finalPrices += this.books[i].price;                // }                for (let book of this.books) {                    finalPrices += book.price;                }                return finalPrices;            },        },    });</script>
  • computed的setter和getter
<div id="app">    <!-- computed计算属性 -->    <h2>{{fullName}}</h2></div><script src="../js/vue.js"></script><script>    const app = new Vue({        el: "#app",        data: {            firstName: "zfcer",            lastName: "best",        },        // 计算属性:在变量没有批改状况下,第一次执行后有缓存        computed: {            // 写法1: 简写            // fullName: function () {            //   return this.firstName + " " + this.lastName;            // },            // 写法2            // 第一种写法是第二种写法的简写            fullName: {                get: function () {                    // 获取返回值:由vue本身来调用get办法                    return this.firstName + " " + this.lastName;                },                set: function (newValue) {                    // 个别computed只读,所以setter办法能够省略                    let names = newValue.split(" ");                    this.firstName = names[0];                    this.lastName = names[1];                },            },        },    });</script>
  • computed与methods属性比照:computed有缓存,methods没有缓存
<div id="app">    <!-- Mustache语法中反对解析办法 -->    <!-- 会执行6次,无缓存。 -->    <h2>{{getFullName()}}</h2>    <h2>{{getFullName()}}</h2>    <h2>{{getFullName()}}</h2>    <h2>{{getFullName()}}</h2>    <h2>{{getFullName()}}</h2>    <h2>{{getFullName()}}</h2>    <!-- computed计算属性 -->    <!-- 会执行1次,有缓存。只有当firstName或lastName产生更改时,才会从新执行 -->    <h2>{{fullName}}</h2>    <h2>{{fullName}}</h2>    <h2>{{fullName}}</h2>    <h2>{{fullName}}</h2>    <h2>{{fullName}}</h2>    <h2>{{fullName}}</h2></div><script src="../js/vue.js"></script><script>    const app = new Vue({        el: "#app",        data: {            firstName: "zfcer",            lastName: "best",        },        // 计算属性:在变量没有批改状况下,第一次执行后有缓存        computed: {            fullName: function () {                console.log("fullName");                return this.firstName + " " + this.lastName;            },        },        // 办法options:每次执行都要调用办法        methods: {            getFullName: function () {                console.log("getFullName");                return this.firstName + " " + this.lastName;            },        },    });</script>

2.9 v-on语法

  • v-on工夫监听根本语法:==在事件监听时,如果办法没有参数,能够省略(); 然而在Mustache语法中是不能省略的==。v-on语法糖@
<div id="app">    <h1>以后计数:{{counter}}</h1>    <button v-on:click="counter++">+</button>    <button v-on:click="counter--">-</button>    <br />    <br />    <!-- v-on的语法糖@ -->    <!-- 留神:在事件监听时,如果办法没有参数,能够省略(); 然而在Mustache语法中是不能省略的 -->    <button @click="add">+</button>    <button @click="sub()">-</button></div><script src="../js/vue.js"></script><script>    const app = new Vue({        el: "#app",        data: {            counter: 0,        },        methods: {            add() {                console.log("add执行了");                // 要用this.                this.counter++;            },            sub() {                this.counter--;            },        },    });</script>
  • v-on工夫监听参数问题:浏览器参数能够通过$event取得;当函数只须要event一个参数时,能够通过省略()的形式取得浏览器产生的event
<div id="app">    <!-- 当监听事件没有参数时,能够省略() -->    <button @click="btn1Click">按钮1</button>    <!-- 当监听事件须要一个参数,然而省略小括号时,Vue会默认将浏览器产生的event工夫对象作为参数传入到办法中 -->    <!-- 不省略()会呈现undefined -->    <button @click="btn2Click()">按钮2-不省略()</button>    <!-- 省略()会获取浏览器event对象 -->    <button @click="btn2Click">按钮2-省略()</button>    <!-- 当监听事件须要蕴含event对象在内的多个参数时,须要借助$event传入event对象 -->    <button @click="btn3Click('zfcer', $event)">按钮3-间接传参</button>    <button @click="btn3Click(name, $event)">按钮3-vue data 传参</button></div><script src="../js/vue.js"></script><script>    const app = new Vue({        el: "#app",        data: {            counter: 0,            name: "best",        },        methods: {            btn1Click() {                console.log("btn1Click ...");            },            btn2Click(event) {                console.log("btn2Click ...", event);            },            btn3Click(name, event) {                console.log("btn2Click ...", name, event);            },        },    });</script>
  • v-on事件监听修饰符

    • .stop - 调用 event.stopPropagation()。
    • .prevent - 调用 event.preventDefault()。
    • .{keyCode | keyAlias} - 只当事件是从特定键触发时才触发回调。
    • .native - 监听组件根元素的原生事件。
    • .once - 只触发一次回调。
<div id="app">    <!-- 1. .stop修饰符阻止冒泡 -->    <div @click="divClick">        aaaaa <br />        <button @click.stop="btn1Click">按钮1</button>    </div>    <br />    <br />    <!-- 2. .prevent修饰符阻止默认提交 -->    <form action="www.baidu.com">        <input type="submit" value="提交" @click.prevent="submitClick" />    </form>    <br />    <br />    <!-- 3. 监听键盘的键帽:当回车键还原时触发 -->    <input type="text" @keyup.enter="keyUp" />    <br />    <br />    <!-- 4. 按钮只触发一次: 理解 -->    <button @click.once="btn2Click">按钮2</button></div><script src="../js/vue.js"></script><script>    const app = new Vue({        el: "#app",        data: {            counter: 0,            name: "best",        },        methods: {            divClick() {                console.log("divClick");            },            btn1Click() {                console.log("btn1Click");            },            submitClick() {                console.log("submitClick");            },            keyUp() {                console.log("keyUp");            },            btn2Click() {                console.log("btn2Click");            },        },    });</script>

2.10 v-if/v-else-if/v-else语法

  • v-if与v-else应用:==v-if与v-else作用在标签上==
<div id="app">    <h2 v-if="isShow">        <span>aaa</span>        <span>bbb</span>        <span>ccc</span>        {{message}}    </h2>    <h2 v-else>isShow为false,不显示...</h2></div><script src="../js/vue.js"></script><script>    const app = new Vue({        el: "#app",        data: {            message: "zfcer",            isShow: true,        },    });</script>
  • v-else-if
<div id="app">    <!-- 个别简单的逻辑是通过computed计算属性来进行解决的 -->    <h2 v-if="score>=90">优良</h2>    <h2 v-else-if="score>=80">良好</h2>    <h2 v-else-if="score>=60">及格</h2>    <h2 v-else>不及格</h2>    <h2>问题体现:{{result}}</h2></div><script src="../js/vue.js"></script><script>    const app = new Vue({        el: "#app",        data: {            message: "zfcer",            score: 92,        },        computed: {            result() {                let resultMessage = "";                if (this.score >= 90) resultMessage = "优良";                else if (this.score >= 80) resultMessage = "良好";                else if (this.score >= 60) resultMessage = "及格";                else resultMessage = "不及格";                return resultMessage;            },        },    });</script>
  • 练习:用户登录形式切换。

    • 留神:==因为虚构DOM的存在,导致两个input控件会复用。==这里须要特地应用key来做辨别,保障登录形式切换后,input不被复用(明码清空)
<div id="app">    <!-- 留神:因为虚构DOM的存在,导致两个input控件会复用。这里须要特地应用key来做辨别,保障登录形式切换后,input不被复用(明码清空) -->    <span v-if="isUser">        <label for="username">用户登录:</label>        <input               type="text"               id="username"               key="username-input"               placeholder="输出用户明码"               />    </span>    <span v-else>        <label for="email">邮箱登录:</label>        <input               type="text"               id="email"               key="email-input"               placeholder="输出邮箱明码"               />    </span>    <button @click="btnClick">切换登录形式</button></div><script src="../js/vue.js"></script><script>    const app = new Vue({        el: "#app",        data: {            isUser: true,        },        methods: {            btnClick() {                this.isUser = !this.isUser;            },        },    });</script>
  • v-if与v-show的区别

    • v-if是真正把标签元素移除了、
    • v-show只是加了个display: none

    留神:==当元素在显示与暗藏之间频繁切换时,举荐应用v-show当元素就是一次切换,举荐应用v-if==

<div id="app">    <!-- 留神:当元素在显示与暗藏之间频繁切换时,举荐应用v-show当元素就是一次切换,举荐应用v-if   -->    <!-- v-if当为false时,会使v-if润饰的元素不在DOM中 -->    <h2 v-if="isShow">{{message}}</h2>    <!-- v-show当为false时, 只是简略的加了display:none 款式 -->    <h2 v-show="isShow">{{message}}</h2></div><script src="../js/vue.js"></script><script>    const app = new Vue({        el: "#app",        data: {            message: "zfcer",            isShow: true,        }    });</script>

2.11 v-for语法

  • v-for遍历数组&对象
<div id="app">    <!-- 数组遍历 -->    <ul>        <li v-for="item in movies">{{item}}</li>    </ul>    <ul>        <li v-for="(item, index) in movies">{{index}} -> {{item}}</li>    </ul>    <br>    <!-- 对象遍历 -->    <ul>        <li v-for="value in info">{{value}}</li>    </ul>    <ul>        <li v-for="(value, key) in info">{{key}} -> {{value}}</li>    </ul>    <ul>        <li v-for="(value, key, index) in info">{{index}} -> {{key}} -> {{value}}</li>    </ul></div><script src="../js/vue.js"></script><script>    const app = new Vue({        el: '#app',        data: {            movies: ['aaa', 'bbb', 'ccc', 'ddd'],            info: {                name: 'zfcer',                age: 23,                height: 17.8            }        }    })</script>
  • v-for中绑定key:绑定惟一的key后让v-for操作更加高效---DIff算法
<div id="app">    <!-- 退出key的次要作用是为了高效更新DOM。然而key肯定要能惟一代表一个item(index显然不行,如果在BC之间插入E,index就会产生扭转)    应用key前:在BC之间插入E,先把C更新为E,D更新为C,最初插入D --- 默认Diff算法    应用key后:间接在BC之间插入E,高效很多 --- 有惟一标识的Diff算法 -->        <!-- 浏览器输出:app.letters.splice(2, 0, 'E')示意在第二个地位后,删除0个元素,插入元素E -->    <ul>        <li v-for="item in letters" :key="item">{{item}}</li>    </ul></div><script src="../js/vue.js"></script><script>    const app = new Vue({        el: '#app',        data: {             letters: ['A', 'B', 'C', 'D'],        }    })</script>

2.12 数组的响应式操作

  • 数组响应式函数:push、pop、shift、unshift、splice、sort、reverse

    • 留神:==间接依据数组下标批改数组元素不是响应式的==
    • 非响应式操作改良伎俩:用splice/set代替
<div id="app">    <ul>        <li v-for="item in letters" :key="item">{{item}}</li>    </ul>    <button @click="btnClick">按钮</button></div><script src="../js/vue.js"></script><script>    const app = new Vue({        el: '#app',        data: {             letters: ['A', 'B', 'C', 'D'],        },        methods: {            btnClick(){                // 响应式操作:push、pop、shift、unshift、splice、sort、reverse                // 1. push                // this.letters.push('aaa', 'bbb', 'ccc')                // 2. pop                // 3. shift 删除第一个元素                // this.letters.shift()                // 4. unshift 从头插入元素                // this.letters.unshift('aaa', 'bbb', 'ccc')                // 5. splice 删除元素/插入元素/替换元素                // this.letters.splice(1, 1, 'aa', 'bb') //能够同时插入多个元素                // 6. sort                // 7. reverse                // 非响应式操作                // this.letters[0] = 'bbbb'                // 解决办法:用splice/set代替                // this.letters.splice(0, 1, 'bbbb')                Vue.set(this.letters, 0, 'bbbb')            }        }    })</script>

2.13 书籍购物车案例

  • style.css
table {    border: 1px solid #e9e9e9;    border-collapse: collapse;    border-spacing: 0;}th,td {    padding: 8px, 16px;    border: 1px solid #e9e9e9;    text-align: left;}th {    background-color: #f7f7f7;    color: #5c6b77;    font-weight: 600;}
  • main.js:==过滤器属性filters==
const app = new Vue({    el: '#app',    data: {        books: [            {                id: 1,                name: 'java study',                date: '2021-7-15',                count: 1,                price: 127.00            },            {                id: 2,                name: 'java study',                date: '2021-7-15',                count: 1,                price: 100.00            },            {                id: 3,                name: 'java study',                date: '2021-7-15',                count: 2,                price: 152.00            },            {                id: 4,                name: 'java study',                date: '2021-7-15',                count: 3,                price: 59.00            }        ]    },    computed: {        totalPrice(){            let allPrices = 0;            // for循环的三种写法            // for(let book of this.books)            //   allPrices += book.price * book.count;            // for(let i = 0; i<this.books.length; i++){            //   allPrices += this.books[i].price * this.books[i].count            // }            // for(let i in this.books){            //   // i为索引            //   allPrices += this.books[i].price * this.books[i].count            // }            // 应用reduce高阶函数求price总和: 箭头函数            allPrices = this.books.reduce((preValue, book) => preValue + book.price * book.count, 0)            return allPrices;        }    },    methods: {        showFixedPrice(price){            return '¥'+price.toFixed(2); //toFixed(2)保留两位小数        },        decrement(index){            if(this.books[index].count == 1)                return;            this.books[index].count--;        },        increment(index){            this.books[index].count++;        },        removeBook(index){            this.books.splice(index, 1);        }    },    //  过滤器:留神它的应用 {{value | showPrice}}    filters: {        showPrice(price){            return '¥'+price.toFixed(2); //toFixed(2)保留两位小数        }    }})
  • index.html
<!DOCTYPE html><html lang="en">    <head>        <meta charset="UTF-8" />        <title>书籍购物车案例</title>        <link rel="stylesheet" href="style.css" />    </head>    <body>        <div id="app">            <table>                <thead v-if="books.length">                    <tr>                        <th>编号</th>                        <th>书籍名</th>                        <th>出版日期</th>                        <th>价格</th>                        <th>数量</th>                        <th>操作</th>                    </tr>                </thead>                <tbody>                    <tr v-for="(book, index) in books" :key="book.id">                        <td>{{book.id}}</td>                        <td>{{book.name}}</td>                        <td>{{book.date}}</td>                        <!-- 函数形式解决价格保留两位小数 -->                        <!-- <td>{{showFixedPrice(book.price)}}</td> -->                        <!-- 过滤器形式解决价格保留两位小数 -->                        <td>{{book.price | showPrice}}</td>                        <td>                            <!-- 绑定disabled属性 -->                          <button @click="decrement(index)" :disabled="book.count <= 1"> - </button>                            {{book.count}}                            <button @click="increment(index)">+</button>                        </td>                        <td><button @click="removeBook(index)">移除</button></td>                    </tr>                </tbody>            </table>            <div v-if="books.length">                <!-- 复用过滤器 -->                <h2>总价格为:{{totalPrice | showPrice}}</h2>            </div>            <div v-else><h2>购物车为空</h2></div>        </div>        <script src="../../js/vue.js"></script>        <script src="main.js"></script>    </body></html>

2.14 v-model语法

  • 表单双向绑定
<div id="app">    <!-- 在输入框上应用v-model就是实现双向绑定 -->    <input type="text" v-model="message" />    <textarea name="" id="" cols="30" rows="10" v-model="message"></textarea>    {{message}}</div><script src="../js/vue.js"></script><script>    const app = new Vue({        el: "#app",        data: {            message: "zfcer",        },    });</script>
  • v-model原理:==v-bind 与 v-on==
<div id="app">    <!-- v-model原理:v-bind 与 v-on -->    <input type="text" v-model="message" />    <input type="text" :value="message" @input="valueChange($event)" />    <!-- 当valueChange不传入参数时,默认会把$event传入 -->    <input type="text" :value="message" @input="valueChange" />    <input           type="text"           :value="message"           @input="message = $event.target.value"           />    <h2>{{message}}</h2></div><script src="../js/vue.js"></script><script>    const app = new Vue({        el: "#app",        data: {            message: "zfcer",        },        methods: {            valueChange(event) {                console.log(event);                this.message = event.target.value;            },        },    });</script>
  • v-model与radio联合应用
<div id="app">    <!-- 在不应用v-model的状况下,肯定要加name属性来保障radio互斥 -->    <label for="male">        <input type="radio" id="male" name="sex" value="男" v-model="sex" />男    </label>    <label for="female">        <input type="radio" id="female" name="sex" value="女" v-model="sex" />女    </label>    <h2>{{sex}}</h2></div><script src="../js/vue.js"></script><script>    const app = new Vue({        el: "#app",        data: {            sex: "女",        },    });</script>
  • v-model与checkbox联合应用
<div id="app">    <!-- 单选框:boolean类型 -->    <label for="agree">        <input               type="checkbox"               name="agree"               value="isAgree"               v-model="isAgree"               />批准协定    </label>    <button :disabled="!isAgree">下一步</button>    <h2>{{isAgree}}</h2>    <br />    <br />    <!-- 复选框:数组类型 -->    <input type="checkbox" value="篮球" v-model="hobbits" />篮球    <input type="checkbox" value="足球" v-model="hobbits" />足球    <input type="checkbox" value="乒乓球" v-model="hobbits" />乒乓球    <input type="checkbox" value="羽毛球" v-model="hobbits" />羽毛球    <h2>{{hobbits}}</h2></div><script src="../js/vue.js"></script><script>    const app = new Vue({        el: "#app",        data: {            isAgree: false,            hobbits: [],        },    });</script>
  • v-model与select联合应用
<div id="app">    <!-- 单选框:boolean类型 -->    <select name="aaa" v-model="fruit">        <option value="苹果">苹果</option>        <option value="橘子">橘子</option>        <option value="香蕉">香蕉</option>    </select>    <h2>{{fruit}}</h2>    <br />    <br />    <br />    <!-- 复选框:数组类型 -->    <!-- 应用multiple变为多选框 -->    <select name="bbb" v-model="fruits" multiple>        <option value="苹果">苹果</option>        <option value="橘子">橘子</option>        <option value="香蕉">香蕉</option>    </select>    <h2>{{fruits}}</h2></div><script src="../js/vue.js"></script><script>    const app = new Vue({        el: "#app",        data: {            fruit: "香蕉",            fruits: [],        },    });</script>
  • v-model与v-bind值绑定:就是动静的给value赋值,如果不应用v-bind:value就会导致value=“ f ”值就是f字符串,而不是fruit
<div id="app">    <!-- 复选框:数组类型 -->    <!-- 通过绑定label for 和 input id实现label与input关联 -->    <label :for="f" v-for="f in orginalFruits">        <input type="checkbox" :id="f" :value="f" v-model="fruits" />{{f}}    </label>    <br />    <br />    <br />    <!-- 复选框:数组类型 -->    <!-- 应用multiple变为多选框 -->    <select v-model="fruits" multiple>        <!-- value属性肯定要应用v-bind:value -->        <option v-for="f in orginalFruits" :value="f">{{f}}</option>    </select>    <h2>{{fruits}}</h2></div><script src="../js/vue.js"></script><script>    const app = new Vue({        el: "#app",        data: {            fruit: "香蕉",            fruits: [],            orginalFruits: ["苹果", "香蕉", "橘子", "葡萄"],        },    });</script>
  • v-model修饰符的应用

    • lazy懒加载
    • number修饰符
    • trim修饰符
<div id="app">    <!-- 1. lazy懒加载:只在input回车/移出焦点下才触发双向绑定 -->    <input type="text" v-model.lazy="message" /> {{message}}    <br />    <br />    <br />    <!-- 2. number,限度input中类型为number类型 -->    <!-- 默认状况下,输入框会被转化为string解决,即便咱们在vue中age: 23,更改age后还是会转为string -->    <!-- 修饰符能够叠加应用 -->    <!-- typeof(value)用来展现value的类型 -->    <input type="text" v-model.number.lazy="age" /> {{age +' age的类型为:'+    typeof(age)}}    <br />    <br />    <br />    <!-- 3. trim,去除多余首尾空格 -->    <input type="text" v-model.trim="message" /> {{message}}</div><script src="../js/vue.js"></script><script>    const app = new Vue({        el: "#app",        data: {            message: "zfcer",            age: 23,        },    });</script>

3 Vue组件化开发


组件化是Vue.js中的重要思维:组件树

3.1 组件化根本应用

  • 组件化应用步骤:1 创立组件结构器对象; 2 注册组件; 3 应用组件

<div id="app">    <!-- 3. 应用组件 -->    <my-cpn></my-cpn>    <my-cpn></my-cpn></div><script class="lazy" referrerpolicy="no-referrer" data-src="../js/vue.js"></script><script>    // 组件化的三步操作    // 1. 创立组件结构器对象    const cpnC = Vue.extend({        template: `            <div>                <h2>我是题目</h2>                <p>我是内容</p>            </div>`,    });    // 2. 注册组件(全局组件)    Vue.component("my-cpn", cpnC);    const app = new Vue({        el: "#app",        data: {            message: "zfcer",        },    });</script>

3.2 全局组件&部分组件

<script>    // 组件化的三步操作    // 1. 创立组件结构器对象    const cpnC = Vue.extend({        template: `            <div>                <h2>我是题目</h2>                <p>我是内容</p>            </div>`,    });    // 2. 注册组件(这里注册的是全局组件,能够在多个Vue实例中应用)    // Vue.component("cpn", cpnC);    const app = new Vue({        el: "#app",        data: {            message: "zfcer",        },        components: {            // 这里注册的是部分组件:作用范畴在div#app标签下            cpn: cpnC,        },    });</script>

3.3 父组件与子组件

<div id="app">    <cpn1></cpn1>    <!-- 子组件因为既没有在全局组件中注册,也没有在Vue实例中注册,故有效! -->    <cpn2></cpn2></div><script src="../js/vue.js"></script><script>    // 1. 两个组件结构器    // 留神:父组件肯定要写在子组件前面,不然就会报找不到子组件异样(父组件须要注册子组件)    // 子组件    const cpnC2 = Vue.extend({        template: `<div><h2>题目2</h2><p>这是内容局部2</p>    </div>`,    });    // 父组件    const cpnC1 = Vue.extend({        template: `<div><h2>题目1</h2><p>这是内容局部1</p><!-- 应用子组件 --><cpn2></cpn2>    </div>`,        components: {            // 注册子组件            cpn2: cpnC2,        },    });    // 2. 注册部分组件    const app = new Vue({        el: "#app",        components: {            // 注册部分组件            cpn1: cpnC1,        },    });</script>

3.4 组件注册语法糖

  • 将Vue.extend({})省略掉,间接在注册组件时传入template
<div id="app">    <!-- 3. 应用组件 -->    <!-- 这种语法糖的注册形式,实质还是进行了Vue.extend({})操作 -->    <cpn1></cpn1>    <cpn2></cpn2></div><script src="../js/vue.js"></script><script>    // 组件化的三步操作    // 1. 创立组件结构器对象:省略构建器对象    const cpnC = Vue.extend({        template: `<div><h2>我是题目</h2><p>我是内容</p>    </div>`,    });    // 2. 注册组件(这里注册的是全局组件,能够在多个Vue实例中应用)    Vue.component("cpn1", {        template: `<div><h2>我是全局组件</h2><p>我是内容</p>    </div>`,    });    const app = new Vue({        el: "#app",        data: {            message: "zfcer",        },        components: {            // 这里注册的是部分组件            cpn2: {                template: `<div><h2>我是部分组件</h2><p>我是内容</p>    </div>`,            },        },    });</script>

3.5 组件模板的拆散写法

  • 举荐应用模板拆散template的形式进行模板拆散
<div id="app">    <!-- 3. 应用组件 -->    <cpn1></cpn1>    <cpn2></cpn2></div><!-- 1. 模板拆散script办法 --><script type="text/x-template" id="cpn1">      <div>        <h2>我是题目</h2>        <p>我是内容</p>    </div></script><!-- 2. 模板拆散template -- 举荐 --><template id="cpn2">    <div>        <h2>我是题目</h2>        <p>我是内容</p>    </div></template><script src="../js/vue.js"></script><script>    // 2. 注册组件(这里注册的是全局组件,能够在多个Vue实例中应用)    Vue.component("cpn1", {        template: "#cpn1",    });    const app = new Vue({        el: "#app",        data: {            message: "zfcer",        },        components: {            // 这里注册的是部分组件            cpn2: {                template: "#cpn2",            },        },    });</script>

3.6 组件中数据寄存问题

  • 组件中数据应用函数形式寄存:防止多个组件实例之间数据共享导致凌乱
<div id="app">    <!-- 3. 应用组件 -->    <!-- data应用函数的作用:划分作用域,当应用多个组件实例时,如果不必data函数,非常容易混同counter变量,导致全局counter呈现 -->    <cpn1></cpn1>    <cpn1></cpn1>    <cpn1></cpn1>    <!-- <cpn2></cpn2> --></div><template id="cpn1">    <div>        <!-- 在模板中应用的数据,必须是在组件中data函数中定义的,如果只是在Vue实例data中注册是读取不到的 -->        <h2>以后计数为:{{counter}}</h2>        <button @click="decrement" :disabled="counter <= 0">-</button>        <button @click="increment">+</button>    </div></template><script src="../js/vue.js"></script><script>    // 定义全局对象    let obj = {        counter: 0,    };    // 2. 注册组件(这里注册的是全局组件,能够在多个Vue实例中应用)    Vue.component("cpn1", {        template: "#cpn1",        data() {            return {                // 在组件中定义counter变量                counter: 0,            };            // return obj; //此时应用的是同一个obj对象,显然不符合要求        },        methods: {            decrement() {                if (this.counter <= 0) return;                this.counter--;            },            increment() {                this.counter++;            },        },    });    const app = new Vue({        el: "#app",        data: {            message: "zfcer123",        },        components: {            // 这里注册的是部分组件            cpn2: {                template: "#cpn1",                data() {                    return {                        message: "zfcer best",                    };                },            },        },    });</script>

3.7 父子组件通信

  • 父传子(props)
<div id="app">    <!-- 实现父传子:v-bind绑定子属性chobbits、cmessage如果不应用v-bind绑定子属性就会把hobbits、message辨认为字符串 -->    <cpn :chobbits="hobbits" :cmessage="message"></cpn></div><template id="cpn">    <!-- template模板外面的内容要用div包裹一下 -->    <div>        <ul>            <li v-for="hobbit in chobbits">{{hobbit}}</li>        </ul>        {{cmessage}}    </div></template><script src="../js/vue.js"></script><script>    // 自定义构造函数PersonName    function PersonName(firstName, lastName) {        this.firstName = firstName;        this.lastName = lastName;    }    // 子组件: prop -- 父传子    const cpn = {        template: "#cpn",        // 1. 间接传值        // props: {        //   cmessage: "",        //   chobbits: [],        // },        // 2. 退出类型检测, 默认值,required        props: {            cmessage: {                // 验证type: String、Number、Boolean、Array、object、Date、Function、Symbol                // 当咱们有自定义构造函数时,验证也反对自定义类型                type: [String, Number, PersonName],                default: "aaaa",                required: true, //示意必须加上这个属性            },            chobbits: {                type: Array,                // default: [], //vue2.5以上,当type是Array/Object时不能间接给数组赋默认值 [] {}                default() {                    return [];                },            },        },        data() {            // 如果写data,必须要加上return,否则就会报错            return {};        },    };    // 把Vue实例看成Root组件    const app = new Vue({        el: "#app",        data: {            message: "zfcer",            hobbits: ["篮球", "足球", "羽毛球"],        },        // 子组件        components: {            // cpn: cpn,            // 加强写法            cpn,        },    });</script>
  • 父传子的驼峰标识问题:v-bind:props属性时,必须把props属性改为驼峰命名
<div id="app">    <!-- 留神:在父传子的时候,因为v-bind不反对驼峰命名,这里的cInfo必须转为c-info -->    <!-- <cpn :cInfo="info"></cpn> -->    <cpn :c-info="info"></cpn></div><template id="cpn">    <!-- template中尽量用div包裹 -->    <div>        <h2>{{cInfo}}</h2>    </div></template><script src="../js/vue.js"></script><script>    const cpn = {        template: "#cpn",        props: {            // 在标签上应用            cInfo: {                type: Object,                defalut() {                    return {};                },                required: true,            },        },    };    const app = new Vue({        el: "#app",        data: {            info: {                name: "zfcer",                age: 23,                height: 1.87,            },        },        components: {            cpn,        },    });</script>
  • 子传父

    • 子组件emit发射出数据
    • 子组件标签中通过属性将数据传递给父组件
<div id="app">    <!-- 利用v-on依据子组件emit出的名字来承受category-click,防止应用驼峰命名没有传入参数是因为能够默认承受发射进去的category对象: 与承受浏览器默认event相似 -->    <cpn @category-click="recive"></cpn>    <div :style="{fontSize:'20px'}">{{result}}</div></div><template id="cpn">    <div>        <button v-for="category in categories" @click="btnClick(category)">            {{category.name}}        </button>    </div></template><script src="../js/vue.js"></script><script>    // 子组件    const cpn = {        template: "#cpn",        data() {            return {                categories: [                    { id: "aaa", name: "热门举荐" },                    { id: "bbb", name: "手机数码" },                    { id: "ccc", name: "家电家用" },                    { id: "ddd", name: "电脑办公" },                ],            };        },        methods: {            btnClick(category) {                // 1. 子组件发射出event,带名字category-click, 这里不要用驼峰命名                // 能够发送多个信息,接管时按程序承受即可                this.$emit("category-click", category, "abc");            },        },    };    const app = new Vue({        el: "#app",        data: {            result: null,        },        components: {            cpn,        },        methods: {            recive(category, name) {                // 父组件接管子组件发射来的category                // 也能够接管多个参数(按程序接管)                console.log(category);                console.log(name);                this.result = category;            },        },    });</script>
  • 父子通信案例:通过props和emit实现版
<!--------props 和 emit 实现版---------><div id="app">    <cpn         :cnum1="num1"         :cnum2="num2"         @num1change="num1Recive"         @num2change="num2Recive"         ></cpn></div><template id="cpn">    <div>        cnum1: {{cnum1}}        <br />        dnum1: {{dnum1}}        <br />        <input type="text" :value="dnum1" @input="dnum1Input" />        <br />        <br />        cnum2: {{cnum2}}        <br />        dnum2: {{dnum2}}        <input type="text" :value="dnum2" @input="dnum2Input" />    </div></template><script src="../js/vue.js"></script><script>    const cpn = {        template: "#cpn",        data() {            return {                dnum1: this.cnum1,                dnum2: this.cnum2,            };        },        props: {            cnum1: {                type: Number,                default: 0,            },            cnum2: {                type: Number,                default: 0,            },        },        methods: {            dnum1Input(event) {                this.dnum1 = parseFloat(event.target.value);                if (event.target.value == null || event.target.value == "")                    this.dnum1 = 0;                this.$emit("num1change", this.dnum1);                this.$emit("num2change", this.dnum1 * 100);            },            dnum2Input(event) {                this.dnum2 = parseFloat(event.target.value);                if (event.target.value == null || event.target.value == "")                    this.dnum2 = 0;                this.$emit("num2change", this.dnum2);                this.$emit("num1change", this.dnum2 / 100);            },        },    };    const app = new Vue({        el: "#app",        data: {            num1: 1,            num2: 2,        },        components: {            cpn,        },        methods: {            num1Recive(dnum1) {                console.log(typeof dnum1);                this.num1 = parseFloat(dnum1);            },            num2Recive(dnum2) {                console.log(typeof dnum2);                this.num2 = parseFloat(dnum2);            },        },    });</script>
  • 父子通信案例:通过watch实现版
<div id="app">    <cpn         :cnum1="num1"         :cnum2="num2"         @num1change="num1Recive"         @num2change="num2Recive"         ></cpn></div><template id="cpn">    <div>        cnum1: {{cnum1}}        <br />        dnum1: {{dnum1}}        <br />        <input type="text" v-model.lazy="dnum1" />        <br />        <br />        cnum2: {{cnum2}}        <br />        dnum2: {{dnum2}}        <input type="text" v-model.lazy="dnum2" />    </div></template><script src="../js/vue.js"></script><script>    const cpn = {        template: "#cpn",        data() {            return {                dnum1: this.cnum1,                dnum2: this.cnum2,            };        },        props: {            cnum1: {                type: Number,                default: 0,            },            cnum2: {                type: Number,                default: 0,            },        },        methods: {            // dnum1Input(event) {            //   this.dnum1 = parseFloat(event.target.value);            //   if (event.target.value == null || event.target.value == "")            //     this.dnum1 = 0;            //   this.$emit("num1change", this.dnum1);            //   this.$emit("num2change", this.dnum1 * 100);            // },            // dnum2Input(event) {            //   this.dnum2 = parseFloat(event.target.value);            //   if (event.target.value == null || event.target.value == "")            //     this.dnum2 = 0;            //   this.$emit("num2change", this.dnum2);            //   this.$emit("num1change", this.dnum2 / 100);            // },        },        watch: {            dnum1(newValue, oldValue) { //函数名与监听的变量名同名,newValue示意监听的新值,oldValue示意监听的旧值                console.log(oldValue)                this.dnum2 = newValue * 100; //dnum2的扭转也会被watch监听到                this.$emit("num1change", newValue);            },            dnum2(newValue) {                this.dnum1 = newValue / 100; //dnum1的扭转也会被watch监听到                this.$emit("num2change", newValue);            },        },    };    const app = new Vue({        el: "#app",        data: {            num1: 1,            num2: 2,        },        components: {            cpn,        },        methods: {            num1Recive(dnum1) {                console.log(typeof dnum1);                this.num1 = parseFloat(dnum1);            },            num2Recive(dnum2) {                console.log(typeof dnum2);                this.num2 = parseFloat(dnum2);            },        },    });</script>

3.8 组件拜访

  • 父拜访子($childern、\$refs)

    • 在组件标签上设置ref属性名后,就能够通过$childern、\$refs实现对子组件数据的拜访。 ==举荐应用&dollar;refs==。
<div id="app">    <cpn></cpn>    <cpn></cpn>    <!-- 用ref属性标识,这样$refs.aaa就能够间接取出这个组件实例 -->    <cpn ref="aaa"></cpn>    <button @click="btnClick">按钮</button></div><template id="cpn">    <div>        <h2>这是组件</h2>    </div></template><script src="../js/vue.js"></script><script>    const app = new Vue({        el: '#app',        components: {            cpn: {                template: '#cpn',                data(){                    return {                        name: '我是cpn组件'                    }                }            }        },        methods: {            btnClick(){                // 1. $childern利用索引下标从数组中取vueComponent元素 -- vueComponent元素示意组件实例                // this.$children不举荐应用,因为当插入新元素后,数组索引下标后扭转                // console.log(this.$children[0].name)                // 2. this.$refs:举荐应用                console.log(this.$refs.aaa.name)                this.$refs.aaa.name = "hahahaahaha"; //批改                console.log('按钮执行了')            }        }    })</script>
  • 子拜访父($parent、\$root)
<div id="app">    <cpn></cpn></div><template id="cpn">    <div>        <h2>这是cpn组件</h2>        <ccpn></ccpn>    </div></template><template id="ccpn">    <div>        <h2>这是ccpn组件</h2>        <button @click="btnClick">按钮</button>    </div></template><script src="../js/vue.js"></script><script>    const app = new Vue({        el: '#app',        data: {            message: 'zfcer root'        },        components: {            cpn: {                template: '#cpn',                data(){                    return {                        name: '我是cpn组件'                    }                },                // 在cpn下                components: {                    ccpn: {                        template: '#ccpn',                        data(){                            return {                                name: '我是ccpn组件'                            }                        },                        methods: {                            btnClick(){                                // 1. this.$parent: 获取以后vueComponent元素的父组件                                // 不举荐应用,个别组件要尽量减少与其余组件的分割                                // console.log(this.$parent)                                // console.log(this.$parent.name)                                // 2. this.$root: 间接获取root组件 -- vue实例                                console.log(this.$root)                                console.log(this.$root.message)                                console.log('按钮执行了')                            }                        }                    }                },            }        },    })</script>

3.9 slot插槽

  • slot根本应用
<div id="app">    <!-- 应用默认标签 -->    <cpn></cpn>    <cpn><span>haha</span></cpn>    <cpn><span>hehe</span></cpn>    <cpn>        <h2>插槽标签1</h2>        <span>插槽标签2</span>        <button>插槽按钮</button>    </cpn>    <cpn></cpn></div><template id="cpn">    <div>        <h2>组件题目</h2>        <span>组件内容</span>        <!-- 定义插槽 -->        <!-- 1. 应用空插槽2. 插槽中能够设置默认标签 button3. 插槽中能够插入多个标签-->        <slot><button>组件按钮</button></slot>    </div></template><script src="../js/vue.js"></script><script>    const cpn = {        template: '#cpn',    }    const app = new Vue({        el: '#app',        data: {            message: 'zfer'        },        components: {            cpn        }    })</script>
  • slot具名插槽
<div id="app">    <!-- 应用默认标签 -->    <cpn></cpn>    <!-- 只能替换没有设置name的插槽 -->    <cpn><span>haha</span></cpn>    <!-- 替换指定插槽 -->    <cpn>        <span slot="left">left</span>        <span slot="center">center</span>        <span slot="right">right</span>    </cpn></div><template id="cpn">    <div>        <!-- 当有多个插槽时,能够给slot设置name属性,来指定slot插入一旦slot设置了name属性,在应用的时候标签中必须应用slot属性指定插槽的name -->        <!-- 默认插槽 -->        <slot><button>组件按钮</button></slot>        <slot name="left"><button>返回</button></slot>        <slot name="center"><input type="text" value="请输出搜寻内容"></slot>        <slot name="right"><button>搜寻</button></slot>    </div></template><script src="../js/vue.js"></script><script>    const cpn = {        template: '#cpn',    }    const app = new Vue({        el: '#app',        data: {            message: 'zfer'        },        components: {            cpn        }    })</script>
  • 作用域插槽

    • 编译的作用域:==父组件模板的所有货色都会在父级作用域内编译;子组件模板的所有货色都会在子级作用域内编译==
    • 作用域插槽的应用:1 template标签上绑定数据languages; 2 组件标签上应用slot-scope获取slotProps
<!-- 父组件替换插槽的标签,然而内容由子组件来提供 --><div id="app">    <cpn></cpn>    <cpn>        <!-- 2. 在应用组件时,最好在template标签下应用slot-scope属性,来获取组件传来的值 -->        <!-- vue2.5.x版本以前必须应用template标签包裹 -->        <template slot-scope="slot">            <!-- join函数 -->            <span>{{slot.languages.join(' * ')}}</span>        </template>    </cpn></div><template id="cpn">    <div>        <!-- 1. 在模板slot上绑定data --- 命名是随便的然而必须应用v-bind -->        <slot :languages="languages">            <ul>                <li v-for="language in languages">{{language}}</li>            </ul>        </slot>    </div></template><script src="../js/vue.js"></script><script>    const app = new Vue({        el: "#app",        components: {            cpn: {                template: "#cpn",                data() {                    return {                        languages: ["java", "vue", "javascript", "c++"],                    };                },            },        },    });</script>

4 Vue CLI详解


4.1 ES6模块化实现

  • 导入形式
// 1.导入的{}中定义的变量import {flag, sum} from "./aaa.js";if (flag) {    console.log('小明是蠢才, 哈哈哈');    console.log(sum(20, 30));}// 2.间接导入export定义的变量import {num1, height} from "./aaa.js";console.log(num1);console.log(height);// 3.导入 export的function/classimport {mul, Person} from "./aaa.js";console.log(mul(30, 50));const p = new Person();p.run()// 4.导入 export default中的内容, 能够省略{}import defaultname from "./aaa.js";defaultname('你好啊');// 5.对立全副导入// import {flag, num, num1, height, Person, mul, sum} from "./aaa.js";import * as aaa from './aaa.js'console.log(aaa.flag);console.log(aaa.height);
  • 导出形式
var name = '小明'var age = 18var flag = truefunction sum(num1, num2) {  return num1 + num2}if (flag) {  console.log(sum(20, 30));}// 1.导出形式一:export {  flag, sum}// 2.导出形式二:export var num1 = 1000;export var height = 1.88// 3.导出函数/类export function mul(num1, num2) {  return num1 * num2}export class Person {  run() {    console.log('在奔跑');  }}// 4.export default// 一个模块内只能有一个export default,不然导出的时候就乱套了const address = '北京市'export default addressexport default function (argument) {  console.log(argument);}

4.2 webpack应用

  • webpack起步

    • 装置webpack,须要node.js的环境

      1. node -v #查看node版本
      2. npm install webpack@3.6.0 -g #全局装置webpack3.6.0
      3. npm install webpack@3.6.0 --save-dev #部分装置webpack3.6.0
    • 留神:在终端间接执行webpack命令,应用的全局装置的webpack;当在package.json中定义了scripts时,其中蕴含了webpack命令,那么应用的是部分webpack。
    • webpack打包指令:webpack src/main.js dist/bundle.js
//1. webpack导入形式const {add, mul} = require('./mathUtils.js')//2. webpack导出形式function add(num1, num2){  return num1+num2}function mul(num1, num2){  return num1*num2}// webpack形式导出module.exports = {  add,  mul}
  • webpack配置

    • 通过npm init生成package.json文件, 并配置脚本

      {    "name": "meetwebpack",    "version": "1.0.0",    "main": "index.js",    "scripts": {        "test": "echo \"Error: no test specified\" && exit 1",        "build": "webpack"    },    "author": "",    "license": "ISC",    "devDependencies": {        "babel-core": "^6.26.3",        "babel-loader": "^7.1.5",        "babel-preset-es2015": "^6.24.1",        "css-loader": "^2.0.2",        "file-loader": "^3.0.1",        "less": "^3.9.0",        "less-loader": "^4.1.0",        "style-loader": "^0.23.1",        "url-loader": "^1.1.2",        "webpack": "^3.6.0"    },    "description": ""}
    • 创立webpack.config.js文件, 配置入口、进口

      // 这里导入的path是node.js中的path: 通过npm init获取node中的path环境const path = require('path')module.exports = {    entry: './src/main.js',    output: {        // __dirname的下划线是两个        path: path.resolve(__dirname, 'dist'),        filename: 'bundle.js'    }}// 通过npm install webpack@3.6.0 --save-dev 装置部分开发webpack,// 最初会生成package.json、package-lock.json文件
  • webpack的loader:在开发中咱们不仅仅有根本的js代码解决,咱们也须要加载css、图片,也包含一些高级的将ES6转成ES5代码,将TypeScript转成ES5代码,将scss、less转成css,将.jsx、.vue文件转成js文件等等。给webpack扩大对应的loader就能够实现上述的性能。

    • loader应用过程:1 npm装置对应的loader(loader能够在webpack官网上找到);2在webpack.config.js中的modules关键字下进行配置;
    • 装置的loader:css-loader、style-loader、less-loader、url-loader、file-loader、ES6语法解决loader(babel-loader、babel-core、babel-preset-es2015)
//package.json中的devDependencies"devDependencies": {    "babel-core": "^6.26.3",    "babel-loader": "^7.1.5",    "babel-preset-es2015": "^6.24.1",    "css-loader": "^2.0.2",    "file-loader": "^3.0.1",    "less": "^3.9.0",    "less-loader": "^4.1.0",    "style-loader": "^0.23.1",    "url-loader": "^1.1.2",    "webpack": "^3.6.0"  },      // webpack.config.js中的moudlemodule: {    rules: [        {            test: /\.css$/i,            // css-loader只负责将css文件进行加载            // style-loader负责将款式增加到DOM中            // 应用多个loader时,是从右往左加载 -- 留神先后顺序            use: ["style-loader", "css-loader"],        },        {            test: /\.less$/i,            loader: [                // compiles Less to CSS                "style-loader",                "css-loader",                "less-loader",            ],        },        {            test: /\.(png|jpg|gif)$/i,            use: [                {                    loader: 'url-loader',                    options: {                        // 当加载的图片,小于limit时,会将图片编译为base64字符串模式 -- 不须要打包到dist中                        // 当加载的图片,大于limit时,须要应用file-loader模块进行加载 -- 须要打包到dist中                        limit: 5632,                        // 人为设置打包到dist文件夹下的图片门路, hash:8标识截取hash的前8位做图片名;ext是图片后缀                        name: 'img/[name].[hash:8].[ext]'                    },                },            ],        },        {            test: /\.js$/,            exclude: /(node_modules|bower_components)/,            use: {                // 利用babel将es6转为es5,                // 装置命令如下:npm install --save-dev babel-loader@7.1.5 babel-core@6.26.3 babel-preset-es2015@6.24.1                loader: 'babel-loader',                options: {                    presets: ['es2015']                }            }        }    ],},    //main.js中导入css、less// 导入base.css文件require('./css/base.css')// 导入.less文件require('./css/special.less')
  • webpack配置vue

    1. 装置vue:npm install vue --save
    2. runtime-only问题:在webpack.config.js中配置

      // 增加vue:runtime-compiler环境resolve: {    alias: {        'vue$': 'vue/dist/vue.esm.js' // 用 webpack 时需用'vue/dist/vue.common.js'    }}
    3. .vue文件封装解决::装置vue-loader、vue-template-compiler
  • webpack配置plugin:loader次要用于转换某些类型的模块,它是一个转换器;plugin是插件,它是对webpack自身的扩大,是一个扩展器。

    • plugin的应用过程:1通过npm装置须要应用的plugins;2在webpack.config.js中的plugins中配置插件
//webpack.config.js// 援用插件plugins: [    new HtmlWebpackPlugin({        template: 'index.html' //为了在dist index.html中配置index.html中的div#app    }),    new UglifyJsPlugin(), //压缩dist中的bundle.js    new webpack.BannerPlugin('最终版权归zfcer所有'),]
  • webpack-server配置

    • 装置webpack-server: npm install --save-dev webpack-dev-server@2.9.1
    • 在webpack.config.js中配置

      devServer: {    contentBase: './dist',    inline: true //inline示意页面实时刷新}
  • 配置拆散

    • 配置base.config.js,在dev.config.js和pro.config.js中导入根本配置

    • 在package.json中应用配置

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

4.3 Vue CLI2

  • 装置Vue CLI: npm install -g @vue/cli@3.0.4npm install -g @vue/cli-init依照Vue CLI2的形式初始化我的项目
  • 初始化我的项目:vue init webpack my-project

  • 目录构造

  • Runtime-Compiler和Runtime-only的区别

    • Vue程序运行过程: template --> ast --> render --> VDOM --> UI

    当咱们在应用.vue文件时,==.vue中的template是由vue-template-compiler来解决的==,所以能够间接应用render来渲染。runtime only ,比runtime compiler少6k空间 (效率更高,空间更小)。

  • npm run build

  • npm run dev

  • webpack.base.conf.js起别名

4.4 Vue CLI3

  • Vue CLI3 与 Vue CLI2区别

    • vue-cli 3 是基于 webpack 4 打造,vue-cli 2 还是 webapck 3
    • vue-cli 3 的设计准则是“0配置”,移除的配置文件根目录下的,build和config等目录
    • vue-cli 3 提供了 vue ui 命令,提供了可视化配置,更加人性化
    • 移除了static文件夹,新增了public文件夹,并且index.html挪动到public中
  • 创立Vue CLI3我的项目: npm create my-project

  • 目录构造

  • vue ui上查看配置:一大堆配置文件被放在node_modules/@vue/cli-service/lib/Service.js文件下

  • 起别名,在我的项目目录下创立vue.config.js文件

5 vue-router


5.1 vue-router的应用

  • 后端路由&前端路由

    • 后端路由:晚期的网站开发整个HTML页面是由服务器来渲染的(JSP)。当咱们页面中须要申请不同的门路内容时, 交给服务器来进行解决, 服务器渲染好整个页面, 并且将页面返回给客户端.p这种状况下渲染好的页面, 不须要独自加载任何的js和css, 能够间接交给浏览器展现, 这样也有利于SEO的优化.
    • 前端路由:

      1. 前后端拆散阶段:后端只提供API来返回数据, 前端通过Ajax获取数据, 并且能够通过JavaScript将数据渲染到页面中。(依据Ajax申请从动态服务器中获取对应的动态资源)
      2. 单页面富利用SPA阶段:SPA最次要的特点就是在前后端拆散的根底上加 了一层前端路由。(依据axios路由申请一次性把所有动态资源获取(路由lazy加载))
  • URL变更

    • location.href来批改url
    • history.pushState({}, '', '/foo')相当于压栈,在浏览器中能够点击返回按钮
    • history.replaceState({}, '', '/foo'), 在浏览器中不能点击返回按钮
    • history.go(-1)弹栈一个元素,等价于history.back() (并不是真正弹栈)
    • history.forward() 则等价于 history.go(1)
  • 装置vue-router:npm install vue-router --save
  • 应用vue-router:1创立路由组件;2配置路由映射:组件与门路映射关系;3应用路由:通过<router-link>和<router-view>

    • 路由嵌套:children: [ ]
    • 参数传递:动静url,'/user/:userId'传递参数userId。在组件中通过this.$route.params.userId来获取
    • 导航守卫:通过在beforeEach中利用to来获取meta来传递导航栏题目, 通过to.matched[0].meta.title来获取meta参数
    • kepp-alive:keep-alive包裹router-view设置拜访缓存, 这样能够让组件created只执行一次(缓存)。组件内的 activated 和 deactived 生命周期函数必须在设置keep-alive标签的包裹下来失效

      • <router-link>: 该标签是一个vue-router中曾经内置的组件, 它会被渲染成一个标签.
      • <router-view>: 该标签会依据以后的门路, 动静渲染出不同的组件.
    //index.jsimport Vue from 'vue'import Router from 'vue-router'// 路由懒加载: 不要间接注册组件const Home = () => import('@/components/Home')const HomeMessages = () => import('@/components/HomeMessages')const HomeNews = () => import('@/components/HomeNews')const About = () => import('@/components/About')const User = () => import('@/components/User')const Profile = () => import('@/components/Profile')// 1. 通过Vue.use(插件),装置插件Vue.use(Router)// 源码剖析:Vue.use(Router)的外部操作是install(Vue类), 在install外部全局注册了RouterView 和 RouterLinkconst routes = [   {      path: '/',      redirect: '/home',      meta: {        title: '首页'      },    },    {      path: '/home',      component: Home,      meta: {        title: '首页'      },      children: [        {          path: '',          component: HomeNews        },        {          path: 'news', //children中的path肯定不能加'/', routes中的path能够加'/'          component: HomeNews        },        {          path: 'messages',           component: HomeMessages        }      ]    },    {      path: '/about',      component: About,      meta: {        title: '对于'      },    },    {      path: '/user/:userId', //注册动静url      component: User,      meta: {        title: '用户'      },    },    {      path: '/profile',      component: Profile,      meta: {        title: '档案'      },    }]// 2. 创立Router对象, 并导出Routerconst router =  new Router({  routes,  mode: 'history', //默认应用的是hash, 这里替换为history形式没有#  linkActiveClass: 'active', //linkActiveClass在路由上设置默认激活款式; active是App.vue中定义的style})router.beforeEach((to, from, next) => {  document.title = to.matched[0].meta.title //获取meta.title  next() //开释router,相似filter成果  console.log(to) // 当存在children时,间接取meta是没有值的。通过打印发现,在matched数组中有要的meta.title})// 3. 将router对象传入的Vue实例中export default router
    //main.jsimport Vue from 'vue'import App from './App'import router from './router' //主动找router/index.jsVue.config.productionTip = false/* eslint-disable no-new */new Vue({  el: '#app',  router, //将router传给Vue类中的$router, 所以router 与 $router是同一个对象  render: h => h(App)})
    <template><div id="app">    <!-- router-link申请path -->    <!-- router-link属性: to示意映射的path;tag示意渲染成其余标签;replace示意应用replaceStatus,让浏览器无奈记录栈,而不能操作返回按钮;active-class当<router-link>对应的路由匹配胜利时, 会主动给以后元素设置一个router-link-active的classrouter-link-active是router-link标签中被选中的元素默认加上的class -->    <!-- <router-link to="/home" tag="button" replace active-class="active">Home</router-link><router-link to="/about" replace active-class="active">About</router-link> -->    <!-- <router-link to="/home" tag="button" replace>Home</router-link><router-link to="/about" replace>About</router-link> -->    <br>    <br>    <br>    <button @click="homeClick">Home</button>    <button @click="aboutClick">About</button>    <!-- <router-link :to="'/user/'+userId">User</router-link><router-link :to="{path: '/profile', query: {name: 'zfcer', age: 23, height: 1.87}}">Profile</router-link> -->    <button @click="userClick">User</button>    <button @click="profileClick">Profile</button>    <!-- router-view占位展现 -->    <!-- keep-alive包裹router-view设置拜访缓存, 这样能够让组件created只执行一次(缓存) -->    <!-- exclude能够用来勾销组件缓存,exclude自身是正则表达式,所以不能有空格 -->    <keep-alive exclude="User">        <router-view/>    </keep-alive>    </div></template><script>    export default {        name: 'App',        data(){            return {                userId: '1206682'            }        },        methods: {            homeClick(){                this.$router.push('/home')                console.log('homeClick run')            },            aboutClick(){                this.$router.push('/about')                console.log('aboutClick run')            },            userClick(){                this.$router.push('/user/' + this.userId)            },            profileClick(){                this.$router.push({                    path: '/profile',                    query: {                        name: 'best',                        age: 18,                        height: 1.87                    }                })            }        }    }    // 1. $router 和 $route 是Vue类的原型中定义的:    //     源码中的install.js中通过object.defineProperty(Vue.prototype, '$router', ...)实现$router 和 $route的注册    // 2. 所有本人创立的组件都继承自Vue类,所以都有$router 和 $route    // 3. 将router (main.js中定义的)传给Vue类中的$router, 所以router 与 $router是同一个对象    // 4. $route 示意以后被激活的route    // $router 与 $route 都来自Vue类中的this._routerRoot对象</script><style>    /* router-link-active是vue默认加上的class */    .active{        color: #f00;    }</style>
  • &dollar;router 和 $route 阐明

    1. $router 和 $route 是Vue类的原型中定义的:源码中的install.js中通过object.defineProperty(Vue.prototype, '$router', ...)实现&dollar;router 和 $route的注册
    2. 所有本人创立的组件都继承自Vue类,所以都有$router 和 $route
    3. 将router (main.js中定义的)传给Vue类中的&dollar;router, 所以router 与 $router是同一个对象
    4. $route 示意以后被激活的route
    5. &dollar;router 与 $route 都来自Vue类中的this._routerRoot对象

5.2 案例tabbar

学会封装组件

6 Vuex详解


6.1 Promise语法

// Promise根本应用new Promise((resolve, reject) => {    setTimeout(() => {        // 胜利的时候调用resolve        resolve("Hello World");        // 失败的时候调用reject        // reject("error message");    }, 1000);})    .then((data) => {    // data是resolve传过来的数据    // 1.100行的解决代码    console.log(data);    console.log(data);    console.log(data);    console.log(data);    console.log(data);})    .catch((err) => {    // err是reject传过来的数据    console.log(err);});// Promise all 实现两个申请同时实现后再进行下一步操作Promise.all([    new Promise((resolve, reject) => {        setTimeout(() => {            resolve({ name: "why", age: 18 });        }, 2000);    }),    new Promise((resolve, reject) => {        setTimeout(() => {            resolve({ name: "kobe", age: 19 });        }, 1000);    }),]).then((results) => {    console.log(results);});

6.2 Vuex应用

Vuex就是为了提供这样一个在多个组件间共享状态的插件,Vuex扮演着大管家的角色(全局单例模式)

  • vuex装置:npm install vuex --save
  • vuex应用步骤:1注册插件; 2创建对象并导出store; 3应用store

    //index.jsimport Vue from 'vue'import Vuex from 'vuex'import {INCREMENT} from '@/store/mutations-types.js'const moduleA = {    state: {      // 模块中的state调用:this.$store.state.a.name      name: 'zfcer'    },    mutations: {      // 模块中的mutations调用: this.$store.commit('mutations名字'), 调用形式不变      updateName(state, payload){        state.name = payload      }    },    actions: {      aUpdateName(context) {        setTimeout(() => {          console.log('模块内actions执行了')          // 模块内的actions只能调用模块内的mutations          context.commit('updateName', '模块内的actions调用了模块内的mutations')        })      }    },    getters: {      // 模块中的getters调用: this.$store.getters.fullName 调用形式不变。所以办法名不要与模块外的办法名反复      fullName(state){        return state.name + '111'      },      fullName2(state, getters){        return getters.name + '222'      },      fullName3(state, getters, rootState){        // rootState获取Root的state        return getters.fullName2 + rootState.counter      }    },     modules: {          }}// 1. 注册插件Vue.use(Vuex)// 2. 创建对象, 并导出export default new Vuex.Store({  state: {    // 共享变量    counter: 1000,    students: [      {id: 110, name: 'zfcer', age: 23},      {id: 111, name: 'best', age: 26},      {id: 112, name: 'one', age: 18},    ],    info: {      name: 'zfcer',      age: 23,      height: 1.87    }  },  mutations: {    // Vuex的store状态的更新惟一形式:提交Mutations    // 办法中是 默认传入state参数的    // mutations中提交参数能够通过payload提交对象    [INCREMENT](state){      state.counter++    },    decrement(state){      state.counter--    },    incrementCountOne(state, payload){      console.log(payload) //payload是一个对象    },    incrementCount(state, payload){      console.log(payload.cnt1 + payload.cnt2)      state.counter += payload.cnt1    },    addStudent(state, stu){      state.students.push(stu)    },    updateInfo(state){      // 间接批改info中已有的属性是响应式的,这是因为Vue对属性做了监听能够做到响应式      state.info.name = 'zfcer best'      // 间接在info中增加新属性不是响应式的      // state.info['address'] = '苏州'      // Vue.set(state.info, 'address', '苏州') //利用Vue.set实现响应式      // 间接删除info中属性不是响应式的      // delete state.info.age      // Vue.delete(state.info, 'age') //利用Vue.delete实现响应式    }  },  actions: {    // actions用来解决异步操作    // 办法一    // context上下文,能够了解为就是store对象    // payload传递参数    // aUpdateInfo(context, payload){    //   console.log("---------------")    //   setTimeout(() => {    //     context.commit('updateInfo')    //     console.log(payload.message)    //     payload.success() //执行回调函数    //   }, 1000)    // }    // 办法二    aUpdateInfo(context, payload){      return new Promise((resolve, reject) => {        setTimeout(() => {          context.commit('updateInfo')          console.log(payload)                    resolve('外部执行胜利') //传递信息能够被内部then获取        }, 1000)      })    }  },  getters: {    // 与计算属性相似    powerCounter(state){      return state.counter * state.counter    },    more20Stus(state){      return state.students.filter(s => s.age >= 20)    },    more20StusLength(state, getters){      // 在getters中能够定义getters参数来取得getters中其余函数      return getters.more20Stus.length    },    moreAgeStus(state){       // 通过外部创立函数来取得moreAgeStus传过来的参数      // return function(age){      //   return state.students.filter(s => s.age >= age)      // }      return age => state.students.filter(s => s.age >= age)    }  },  modules: {    // 模块最终会被加载到state中,还是一个实体    a: moduleA  }})// 对象的解构const obj = {  name: 'zfcer',  age: 18,  height: 1.87}const {name, height, age} = obj// 数组解构const names = ['zfcer', 'best', 'one']const [name1, name2, name3] = names
    // main.jsimport Vue from 'vue'import App from './App'import router from './router'import store from './store'Vue.config.productionTip = false/* eslint-disable no-new */new Vue({  el: '#app',  router,  store, //3. 应用store  render: h => h(App)})
  • vuex我的项目构造

7 axios


  • 全局Axios
// 一、间接用axios是用的全局Axios// axios全局配置axios.defaults.baseURL = 'http://152.136.185.210:7878/api/m5'axios.defaults,headers.post['Content-Type'] = 'application/x-www-form-urlencoded'axios.defaults.timeout = 5000// 1. axios根本应用// 指定method的申请,默认是get申请axios({  // 部分配置  baseURL: 'http://152.136.185.210:7878/api/m5',  url: '/home/multidata',  method: 'get'}).then(res => console.log(res))// get申请axios.get('/home/multidata')// 带参数的get申请axios({  url: '/home/data',  // 针对get申请的参数拼接  params: {    type: 'pop',    page: 1  }}).then(res => console.log(res))// 2. axios并发应用// then中获取后果集axios.all([axios({  url: '/home/multidata',}), axios({  url: '/home/data',  // 针对get申请的参数拼接  params: {    type: 'sell',    page: 5  }})]).then(res => {  //合并后的后果  console.log(res)})// then中获取别离的后果axios.all([axios({  url: 'http://123.207.32.32:8000/home/multidata',}), axios({  url: 'http://152.136.185.210:7878/api/m5/home/data',  // 针对get申请的参数拼接  params: {    type: 'sell',    page: 5  }})]).then(axios.spread((res1, res2) => {  //两个异步申请别离的后果  console.log(res1)  console.log(res2)}))
  • 部分Axios
//  二、axios实例创立与应用 const instance = axios.create({  baseURL: 'http://152.136.185.210:7878/api/m5',  timeout: 5000,  headers: {    'Content-Type': 'application/x-www-form-urlencoded'  }})instance({  url: '/home/data',  method: 'get'}).then(res => {  console.log(res)}).catch(err => {  console.log(err)})
  • 封装axios:创立network文件夹-->request.js工具类,在main.js中调用封装的request
//--------------------request.js---------------------------import axios from 'axios'export function request1(config, success, failure){  // 1.创立axios实例  const instance = axios.create({    baseURL: 'http://152.136.185.210:7878/api/m5',    timeout: 5000  })  // 2.1.axios拦截器  instance.interceptors.request.use(config => {    console.log(config)    // (1)config中一些信息不合乎服务器要求须要拦挡    // (2)每次发送网络申请时,都心愿在界面中显示一个申请的图标    // (3)某些网络申请(比方登录token),必须携带一些非凡信息    return config //开释拦挡,如果不开释拦挡就无奈继续执行  }, err => {    console.log(err)  })   // 2.2.axios响应拦挡  instance.interceptors.response.use(res => {    console.log(res)    // 做一些响应解决拦挡    return res.data //只须要把data信息返回即可  }, err => {    console.log(err)  })  // 3.发送真正的网络申请  instance(config)  .then(res => {    // console.log(res);    success(res)  })  .catch(err => {    // console.log(err)    failure(err)  })}export function request2(config){  // 创立axios实例  const instance = axios.create({    baseURL: 'http://152.136.185.210:7878/api/m5',    timeout: 5000  })  // 发送真正的网络申请  instance(config.baseConfig)  .then(res => {    // console.log(res);    config.success(res)  })  .catch(err => {    // console.log(err)    config.failure(err)  })}export function request3(config) {  return new Promise((resolve, reject) => {    // 创立axios实例    const isntance = axios.create({      baseURL: 'http://152.136.185.210:7878/api/m5',      timeout: 5000    })    // 发送真正的网络申请    instance(config)    .then(res => {      resolve(res)    })    .catch(err => {      reject(err)    })  })}export function request4(config) { // 创立axios实例  const instance = axios.create({    baseURL: 'http://152.136.185.210:7878/api/m5',    timeout: 5000  })  // 发送真正的网络申请  return instance(config) //因为axios实例自身就是Promise}//-------------------main.js----------------------------// 三、axios封装import {request1, request2, request3, request4} from '@/network/request.js'request1({  url: '/home/multidata'}, res => {  console.log(res)}, err => {   console.log(err)})request2({  baseConfig: '/home/multidata',  success: function(res){    console.log(res)  },  failure: function(err){    console.log(err)  }})request3({  url: '/home/multidata'}).then(res => console.log(res)).catch(err => console.log(err))request4({  url: '/home/multidata'}).then(res => console.log(res)).catch(err => console.log(err))

8 我的项目实战


将本地我的项目与近程仓库关联起来:

git remote add origin https://github.com/zfcer/testmall.gitgit push -u origin master

8.1 我的项目根本设置

  • 我的项目构造

  • 设置css款式和全局款式

    • initialize.css:normalize.css,能够在github中查看normalize.css
    • base.css:定义全局款式

  • vue.config.js 全局配置:起别名

    module.exports = {  configureWebpack: {    resolve: {      alias: {        'assets': '@/assets',        'common': '@/common',        'components': '@/components',        'network': '@/network',        'views': '@/views',      }    }  }}
  • .editorconfig 全局配置我的项目编辑

    root = true[*]charset = utf-8indent_style = spaceindent_size = 2end_of_line = lfinsert_final_newline = truetrim_trailing_whitespace = true

8.2 tabbar&导航栏组件

  • tabbar组件

    1. 导入写好的TabBar、TabBarItem、MainTabBar组件
    2. 装置vue-router,编写index.js路由,最初在mian.js中注入router
    3. 编写对应的views
    4. 在App.vue中引入组件,template中应用组件
  • 导航栏组件

    1. 创立通用组件NarBar, 定义三个slot(left、center、right)
    2. 在views中应用NarBar组件

8.3 首页 - 轮播图&举荐组件

  • axios封装
  • 轮播图

    1. 创立swiper通用组件
    2. 在home文件夹下创立childComps/HomeSwiper(应用swiper通用组件)
    3. 在Home.vue通过axios获取数据,在HomeSwiper中通过props获取数据并展现
    4. 在Home.vue中应用HomeSwiper组件
  • 举荐组件

    1. 在/src/views/home/childCops 创立 RecommendView.vue 组件
    2. 在Home.vue通过axios获取数据,在RecommendView中通过props获取数据并展现
    3. 在Home.vue中应用RecommendView组件