乐趣区

关于vue.js:vue

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 self
vm._self = vm
initLifecycle(vm)
initEvents(vm)
initRender(vm)
callHook(vm, 'beforeCreate')
initInjections(vm) // resolve injections before data/props
initState(vm)
initProvide(vm) // resolve provide after data/props
callHook(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 组件
退出移动版