Vue课堂笔记

第一天:

APP类型

  1. webAPP -> 移动端/M站
  2. 原生APP -> 通过IOS或Android语言开发的
  3. hybridAPP -> webapp + 原生APP

什么是Vue

Vue有两个核心点:
  1. 响应式
  2. 组件
其他优点:
  1. 易用
  2. 虚拟DOM
  3. 双向数据绑定
  4. 体积小
  5. 生态圈繁荣、学习成本低
SEO -> 搜索引擎优化

第二天

选项对象
<div id="app">
    <p v-on:click="clickHandle">{{ message }}</p>
</div>
<script>
    const vm = new Vue({
        el: '#app', // 挂载点
        data: { // 咱们的数据 这里是和模板发生关系的地方
            message: 'hi Vue'
        },
        methods: {
            clickHandle () {
                alert('hello, Vue!')
            }
        }
    })
</script>
methods反转字符串
<div id="app">
    <p>{{ message }}</p>
    <button v-on:click="reverseMessage">反转字符串</button>
</div>
<script>
    new Vue({
        el: '#app',
        data: {
            message: 'hello, Vue!'
        },
        methods: {
            reverseMessage () {
                // console.log(this.message.split(''))
                this.message  = this.message.split('').reverse().join('')
            }
        }

    })
</script>
{{}}只能使用表达式
<div id="app">
<!--    <p>{{ if(flag) { 'yes' } else { 'no' } }}</p>-->
    <p>{{ flag ? 'yes' : 'no' }}</p>
    <!-- {{ 只能使用JS表达式,不能使用JS语句 }} -->
</div>
<script>
    const vm = new Vue({
        el: '#app', // 挂载点
        data: { // 咱们的数据 这里是和模板发生关系的地方
            flag: false
        }
    })

    // if(flag) {
    //
    // } else {
    //
    // }
    // let a = 1;
    // let b = 2
    // console.log(a + b) //
</script>
v-html
<div id="app">
    <p v-html="message"></p>
</div>
<script>
    const vm = new Vue({
        el: '#app', // 挂载点
        data: { // 咱们的数据 这里是和模板发生关系的地方
            message: '

\n' + ' hello 1902B\n' + '

'
} }) </script>
v-if VS v-show
基本差别
  1. v-if动态创建标签,如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。
  2. v-show通过css中的display来动态控制显示或隐藏,不管初始条件是什么,元素总是会被渲染
使用场景

一般来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件很少改变,则使用 v-if 较好。

  1. v-show一般会用在切换比较频繁
  2. v-if切换不频繁的
v-if 、 v-else 、 v-else-if
template包裹元素,不会出现在DOM结构中
<div id="app">
    <template v-if="seen">
        <p>AAA</p>
        <p>AAA</p>
        <p>AAA</p>
    </template>
</div>
<script>
    const vm = new Vue({
        el: '#app', // 挂载点
        data: { // 咱们的数据 这里是和模板发生关系的地方
            seen: true
        }
    })
</script>

v-for ☆

遍历数组
<div id="app">
    <ol>
        <li v-for="item in todos">{{ item.text }}</li>
    </ol>
</div>
<script>
    const vm = new Vue({
        el: '#app', // 挂载点
        data: { // 咱们的数据 这里是和模板发生关系的地方
            todos: [
                {
                    text: 'JavaScript'
                },
                {
                    text: 'React'
                },
                {
                    text: 'Vue2.0'
                }
            ]
        }
    })
</script>

遍历对象

<div id="app">
    <ol>
        <li v-for="value in obj">{{ value }}</li>
    </ol>
</div>
<script>
    const vm = new Vue({
        el: '#app', // 挂载点
        data: { // 咱们的数据 这里是和模板发生关系的地方
           obj: {
               firstName: '二师兄',
               lastName: '猪八戒',
               age: '咱也不知道'
           }
        }
    })
</script>

v-if VS v-for
v-for的优先级高于v-if 和 v-show

<div id="app">
    <ol>
        <li v-for="item in arr" v-if="item.flag">{{ item.title }}</li>
    </ol>
</div>
<script>
    const vm = new Vue({
        el: '#app', // 挂载点
        data: { // 咱们的数据 这里是和模板发生关系的地方
           arr: [
               {
                   title: '熊大',
                   flag: true
               },
               {
                   title: '熊二',
                   flag: true
               },
               {
                   title: '强哥',
                   flag: false
               }
           ]
        }
    })
</script>

v-class
v-bind:class 指令也可以与普通的 class 属性共存

<div id="app">
    <span class="fontSize" v-bind:class="{ red: addClass }">hello, Vue</span>
</div>
<script>
    const vm = new Vue({
        el: '#app', // 挂载点
        data: { // 咱们的数据 这里是和模板发生关系的地方
           addClass: true
        }
    })
</script>

v-bind:style

<div id="app">
    <span v-bind:style="styleObj">hello, Vue</span>
</div>
<script>
    const vm = new Vue({
        el: '#app', // 挂载点
        data: { // 咱们的数据 这里是和模板发生关系的地方
           styleObj: {
               color: 'red',
               fontSize: '100px'
           }
        }
    })
</script>

过滤器

本地(局部)过滤器

<div id="app">
    <p>{{ msg | firstUpper }}</p>
</div>
<script>
    new Vue({
        el: '#app',
        data: {
           msg: 'abc'
        },
        // 定义局部过滤器
        filters: {
            firstUpper(val) {
                return val.charAt(0).toUpperCase() + val.slice(1)
            }
        }
    })
</script>

全局过滤器

<div id="app">
    <p>{{ msg | firstUpper }}</p>
</div>
<script>
    // 全局过滤器
    Vue.filter('firstUpper', val => {
        return val.charAt(0).toUpperCase() + val.slice(1)
    })
    new Vue({
        el: '#app',
        data: {
           msg: 'abc'
        }
    })
</script>

过滤器传参

<div id="app">
    <p>{{ msg | firstUpper('1') }}</p>
</div>
<script>
    // 全局过滤器
    Vue.filter('firstUpper', (val, index) => {
        return val.slice(0, index) + val.charAt(index).toUpperCase() + val.slice(Number(index) + 1)
    })
    new Vue({
        el: '#app',
        data: {
           msg: 'abc'
        }
    })
</script>

过滤器的串联

<div id="app">
    <p>{{ msg | firstUpper('1') | connect }}</p>
</div>
<script>
    // 全局过滤器
    Vue.filter('firstUpper', (val, index) => {
        return val.slice(0, index) + val.charAt(index).toUpperCase() + val.slice(Number(index) + 1)
    })
    Vue.filter('connect', val => {
        return val.split('').join('-')
    })
    new Vue({
        el: '#app',
        data: {
           msg: 'abc'
        }
    })
</script>
作业

商品分类
API:https://api.it120.cc/small4/shop/goods/category/all

第三天

自定义指令

除了核心功能默认内置的指令 (v-model 和 v-show),Vue 也允许注册自定义指令。注意,在 Vue2.0 中,代码复用和抽象的主要形式是组件。然而,有的情况下,你仍然需要对普通 DOM 元素进行底层操作,这时候就会用到自定义指令

基本用法
<div id="app">
    <input type="text" v-focus>
</div>
<script>
    Vue.directive('focus', {
        inserted(el) {
            console.log(el)
            el.focus()
        }
    })
    new Vue({
        el: '#app'
    })
</script>

钩子

<div id="app">
    <div v-smallfour>{{ num }}</div>
    <div>
    <button @click="add">Add</button>
</div>
    <button onclick="unbind()">unbind</button>
</div>
<script>
    // 自定义指令
    Vue.directive("smallfour",{
        bind:function(el,binding){	//被绑定
            console.log('1 - bind');
        },
        inserted: function () {		//绑定到节点
            console.log('2 - inserted');
        },
        update: function () {		//组件更新
            console.log('3 - update');
        },
        componentUpdated: function () {		//组件更新完成
            console.log('4 - componentUpdated');
        },
        unbind: function () {		//解绑
            console.log('5 - unbind');
        }

    });

    // 解绑
    function unbind() {
        vm.$destroy();  //解除绑定
    }

    // 实例化
    const vm = new Vue({
        el : '#app',
        data : {
            num: 0
        },
        methods: {
            add: function () {
                this.num++;
            }
        }
    })
</script>

扩展知识
官网

大前端

修饰符

事件修饰符
  1. stop
  2. capture
  3. self
  4. once
按键修饰符
  1. enter
  2. tab
  3. delete
  4. esc
V-model

checkbox
<div id="app">
    <input type="checkbox" value="熊大" v-model="checkedNames">
    <label for="">熊大</label>
    <input type="checkbox" value="熊二" v-model="checkedNames">
    <label for="">熊二</label>
    <input type="checkbox" value="强哥" v-model="checkedNames">
    <label for="">强哥</label>
    <br />
    <span>checked names: {{ checkedNames }}</span>
</div>
<script>
    new Vue({
        el: '#app',
        data: {
            checkedNames: []
        }
    })
</script>
radio
<div id="app">
    <input type="radio" value="熊大" v-model="picked">
    <label for="">熊大</label>
    <input type="radio" value="熊二" v-model="picked">
    <label for="">熊二</label>
    <input type="radio" value="强哥" v-model="picked">
    <label for="">强哥</label>
    <br />
    <span>picked: {{ picked }}</span>
</div>
<script>
    new Vue({
        el: '#app',
        data: {
            picked: ''
        }
    })
</script>
select
<div id="app">
    <select v-model="selected">
        <option v-for="option in options" :value="option.value">
            {{ option.text }}
        </option>
    </select>
    <span>Selected: {{ selected }}</span>
</div>
<script>
    new Vue({
        el: '#app',
        data: {
            selected: 'B',
            options: [
                { text: 'One', value: 'A' },
                { text: 'Two', value: 'B' },
                { text: 'Three', value: 'C' }
            ]
        }
    })
</script>
数组更新检测

Vue中数组的方法是变异的方法,因为和原生JS相比增加了响应式的特性;

作业

表单-v-model

computed 计算属性

模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护

<div id="app">
    <p>{{ message }}</p>
    <p>{{ reverseMessage }}</p>
</div>
<script>
    new Vue({
        el: '#app',
        data: {
            message: 'React'
        },
        computed: {
            reverseMessage() {
                return  this.message.split('').reverse().join('')
            }
        }
    })
</script>
methods

<div id="app">
    <p>{{ message }}</p>
    <p>{{ reverseMessage() }}</p>
</div>
<script>
    new Vue({
        el: '#app',
        data: {
            message: 'angularJS'
        },
        computed: { // 只要计算属性依赖的data里面的某个值没有发生任何变化,处理数据的逻辑代码就不再执行了,而是直接返回上次计算的结果
            reverseMessage() {
                return  this.message.split('').reverse().join('')
            }
        }
        // methods: { // methods每次都会执行一遍,没有缓存
        //     reverseMessage() {
        //         return this.message.split('').reverse().join('')
        //     }
        // }
    })
</script>
watch

<div id="app">
    <input type="text" v-model="firstName">
    <input type="text" v-model="lastName">
    <input type="text" v-model="fullName">
</div>
<script>
    new Vue({
        el: '#app',
        data: {
            firstName: 'Foo',
            lastName: 'Bar',
            fullName: 'Foo Bar'
        },
        watch: {
            // 监听firstName这个数据
            firstName: function(val) {
                console.log(val)
                this.fullName = val + '' + this.lastName
            },
            // 监听firstName这个数据
            lastName: function(val) {
                console.log(val)
                this.fullName = this.firstName + '' + val
            }
        }
    })
</script>
computed同样可以使用更加简便的方法实现和watch同样的效果

<div id="app">
    <input type="text" v-model="firstName">
    <input type="text" v-model="lastName">
    <input type="text" v-model="fullName">
</div>
<script>
    new Vue({
        el: '#app',
        data: {
            firstName: 'Foo',
            lastName: 'Bar'
        }
        computed: {
            // fullName定义的计算属性名
            fullName: function() {
                return this.firstName + '' + this.lastName
            }
        }
    })
</script>

watch的使用场景/watch的特点
只有监听的值变化的时候才执行,默认不执行

<div id="app">
   <p>fullName: {{ fullName }}</p>
   <p>firstName: <input type="text" v-model="firstName"></p>
</div>
<script>
    new Vue({
        el: '#app',
        data: {
            firstName: 'Foo',
            lastName: 'Bar',
            fullName: ''
        },
        watch: {
            firstName(newName, oldName) {
                this.fullName = newName + '' + this.lastName
            }
        }
    })
</script>

初始化执行一次,需要配置一个属性immerdiate

<div id="app">
   <p>fullName: {{ fullName }}</p>
   <p>firstName: <input type="text" v-model="firstName"></p>
</div>
<script>
    new Vue({
        el: '#app',
        data: {
            firstName: 'Foo',
            lastName: 'Bar',
            fullName: ''
        },
        watch: {
            firstName: {
                // 类似于钩子
                handler(newName, oldName) {
                    this.fullName = newName + '' + this.lastName
                },
                immediate: true // 初始化执行一次
            },

        }
    })
</script>

deep 深度监听

<div id="app">
   <p>obj.title: <input type="text" v-model="obj.title"></p>
</div>
<script>
    new Vue({
        el: '#app',
        data: {
            obj: {
                title: '熊大'
            }
        },
        watch: {
            obj: {
                handler(newName, oldName) {
                   console.log('obj.title改变了')
                },
                // immediate: true, // 初始化执行一次
                deep: true // 深度监听
            },

        }
    })
</script>
作业

TODOlist

下周预告:检查上周所有作业

第四天 - 组件

组件特性

  • 便于协同开发

  • 方便复用

  • 提升整个项目的可维护性

  • 可定制

  • 互操作性 - 多组件互相协同

  • 高内聚

  • 低耦合

组件的基本构成

  • 样式结构

  • 行为逻辑

  • 数据

组件封装

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="../vue.js"></script>
</head>
<body>
<div id="app">
    <custom-select></custom-select>
</div>
<script>
    // 注册组件
    Vue.component('custom-select', {
        template: `
  • JavaScript7
  • React16
  • Vue2.0
  • JavaScript5
  • JavaScript6
`
}) // 实例化 new Vue({ el: '#app' }) </script> </body> </html>
组件通信

父传子 - 上到下
基础示例 - 一级传递 - 嵌套一层而已

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="../vue.js"></script>
</head>
<body>
<div id="app">
    <custom-select :btn-val="btn1"></custom-select>
    <custom-select :btn-val="btn2"></custom-select>
</div>
<script>
    // 注册组件 - 子组件
    Vue.component('custom-select', {
        props: ['btnVal'],
        template: `
  • JavaScript7
  • React16
  • Vue2.0
  • JavaScript5
  • JavaScript6
`
}) ​ ​ // 实例化 - 看做父组件 new Vue({ el: '#app', data: { btn1: '查询', btn2: '搜索' } }) </script> </body> </html>

进阶示例 - 多层嵌套 - 多层传递

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="../vue.js"></script>
</head>
<body>
<div id="app">
    <custom-select :btn-val="btn1" :list="list1"></custom-select>
    <custom-select :btn-val="btn2" :list="list2"></custom-select>
</div>
<script>
    // 注册组件 - 父组件
    Vue.component('custom-select', {
        props: ['btnVal', 'list'],
        template: `
`
})// 注册组件 - 孙子组件 Vue.component('list', { props: ['childList'], template: `
  • {{ item }}
`
}) ​ ​ // 实例化 - 看做爷爷组件 new Vue({ el: '#app', data: { btn1: '查询', btn2: '搜索', list1: ['猪八戒','孙猴子', '沙和尚'], list2: ['熊大', '熊二', '强子'] } }) </script> </body> </html>
  • 第一步:在组件的标签上面以自定义属性名 + 数据的形式(),进行数据的绑定;

  • 第二步:然后在组件内部通过props进行接收(props:[‘list’]),就可以直接在template中进行数据的使用了

子传父 - 下到上
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="../vue.js"></script>
</head>
<body>
<div id="app">
    <custom-select :btn-val="btn1" :list="list1"></custom-select>
    <custom-select :btn-val="btn2" :list="list2"></custom-select>
</div>
<script>
    // 注册组件 - 父组件
    Vue.component('custom-select', {
        props: ['btnVal', 'list'],
        data() {
            return {
                value: ''
            }
        },
        template: `
`
, methods: { getVal(val) { console.log(`${val}我是子组件传递过来的数据`) this.value = val } } }) ​ ​ // 注册组件 - 孙子组件 Vue.component('list', { props: ['childList'], template: `
  • {{ item }}
`
, methods: { selectVal(val) { // console.log(val) // 把数据传递给父组件 this.$emit('receive', val) // 第一个参数:自定义事件名;第二个参数:要传递的数据; } } }) ​ ​ // 实例化 - 看做爷爷组件 new Vue({ el: '#app', data: { btn1: '查询', btn2: '搜索', list1: ['猪八戒','孙猴子', '沙和尚'], list2: ['熊大', '熊二', '强子'] } }) </script> </body> </html>
  • 第一步:在子组件上通过this下面的$emit(第一个参数是自定义事件,第二个参数要传递的数据)

  • 第二步:在子组件的组件标签上通过@自定义事件名,等于回调函数的形式进行数据的处理

兄弟组件 - 同级组件
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="../vue.js"></script>
</head>
<body>
<div id="app">
    <custom-select :btn-val="btn1" :list="list1"></custom-select>
</div>
<script>
    // 注册组件 - 父组件
    Vue.component('custom-select', {
        props: ['btnVal', 'list'],
        data() {
            return {
                value: ''
            }
        },
        template: `
`
, methods: { getVal(val) { console.log(`${val}我是子组件传递过来的数据`) this.value = val } } })// 注册组件 - 兄弟组件input Vue.component('child-input', { props: ['childInput', 'childListItem'], template: `
`
})// 注册组件 - 兄弟组件list Vue.component('list', { props: ['childList'], template: `
  • {{ item }}
`
, methods: { selectVal(val) { // console.log(val) // 把数据传递给父组件 this.$emit('receive', val) // 第一个参数:自定义事件名;第二个参数:要传递的数据; } } }) ​ ​ // 实例化 - 看做爷爷组件 new Vue({ el: '#app', data: { btn1: '查询', btn2: '搜索', list1: ['猪八戒','孙猴子', '沙和尚'], list2: ['熊大', '熊二', '强子'] } }) </script> </body> </html>
  • 第一:兄弟组件通信是利用父组件作为中转;

  • 第二:子 -> 父 -> 子

课后练习题

组件通信重写TODO - 习题素材下载

第五天

为什么组件中的data必须是一个函数

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<script>
    // const myComponent = function() {}
    // // 这个data在这里是一个对象
    // myComponent.prototype.data = {
    //     a: 1,
    //     b: 2
    // }
    // const component1 = new myComponent()
    // const component2 = new myComponent()
    //
    // component1.data.a = 5
    //
    // console.log(component2.data.a)
    /*
    * 如果是对象,不用function,返回每个组件的data都是内存中的同一个地址,一个数据改变了,其他的也会发生变化。
    *
    * 在JavaScript只有函数可以构成作用域,这就是data为什么是一个函数的原因,每个组件实例,都有自己的作用域,互相独立不会互相影响
    *
    * */const myComponent = function() {
        this.data = this.data()
    }
    myComponent.prototype.data = function() {
        return {
            a: 1,
            b: 2
        }
    }const component1 = new myComponent()
    const component2 = new myComponent()
​
    component1.data.b = 5
    console.log(component2.data.b)</script>
</body>
</html>
跨组件通信

是通过Vue空实例,作为连接$emit & $on,又称为中央通信

目前中央通信是解决兄弟间通信,祖父祖孙间通信的最佳方法,不仅限于此,也可以解决父组件子组件间的相互通信

  • *各组件可自己定义好组件内接收外部组件的消息事件即可,不用理会是哪个组件发过来;而对于发送事件的组件,亦不用理会这个事件到底怎么发送给我需要发送的组件。

  • *Bus中央通信的方案各种情况下都可用,比较方便

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="../vue.js"></script>
</head>
<body>
<div id="app">
    <Aaa></Aaa>
    <Bbb></Bbb>
</div>
<script>
    // Vue空实例 中央通信 用于连接$emit & $on
    let bus = new Vue()// 组件A
    Vue.component('Aaa', {
        template: `
我是A组件
`
, methods: { aaa() { bus.$emit('is-selected') // 广播 } } })// 组件B Vue.component('Bbb', { template: `
我是B组件
`
, created() { bus.$on('is-selected', function() { // 接收/监听 console.log('我是A组件传递过来的') }) } })new Vue({ el: '#app' }) </script> </body> </html>
单向数据流

数据从父组件传递给子组件,只能单向绑定。

在子组件内部不能直接修改父组件传递过来的数据。

注意:最新的Vue2.6版本,不会警告可以直接修改父组件传递过来的数据,简单查了一下没找到相关资料。按照2.0的理解去记就可以了,知道2.6这个特性即可。

不能直接改变父组件传递过来的数据

<div id="app">
    <custom-com :count="count"></custom-com>
</div>
<script>
    //子组件
    Vue.component('customCom', {
        props: ['count'],
        template: `

我是自定义的组件

{{ count }}

`
, methods: { changeCount() { this.count++ } } })// 看做父组件 new Vue({ el: '#app', data: { count: 0 } }) </script>

第一种修改方式:父组件传递过来的数据作为子组件局部的初始值

<div id="app">
    <custom-com :count="count"></custom-com>
</div>
<script>
    //子组件
    Vue.component('customCom', {
        props: ['count'],
        data() {
            return {
                increment: this.count
            }
        },
        template: `

我是自定义的组件

{{ increment }}

`
, methods: { changeCount() { this.increment++ } } })// 看做父组件 new Vue({ el: '#app', data: { count: 0 } }) </script>

第二种:通过computed

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="../vue.js"></script>
</head>
<body>
<div id="app">
    <custom-com :count="count"></custom-com>
</div>
<script>
    //子组件
    Vue.component('customCom', {
        props: ['count'],
        data() {
            return {
                increment: this.count
            }
        },
        template: `

我是自定义的组件

{{ incrementCount }}

`
, methods: { changeCount() { this.increment++ } }, computed: { incrementCount() { return this.increment } } })// 看做父组件 new Vue({ el: '#app', data: { count: 0 } }) </script> </body> </html>
Prop验证

我们可以为组件的 prop 指定验证要求

内置校验规则

  • String

  • Number

  • Boolean

  • Array

  • Object

  • Date

  • Function

  • Symbol

自定义校验规则

props: {
            count: {
               validator: function(value) {
                   return value < 10
               }
            }
        },

示例

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="../vue.js"></script>
</head>
<body>
<div id="app">
    <custom-com :count="count"></custom-com>
</div>
<script>
    //子组件
    Vue.component('customCom', {
        // props: {
        //     count: {
        //         type: [Number, String],
        //         default: 100
        //     }
        // },
        props: {
            count: {
               validator: function(value) {
                   return value < 10
               }
            }
        },
        data() {
            return {
                increment: this.count
            }
        },
        template: `

我是自定义的组件

{{ increment }}

`
, methods: { changeCount() { this.increment++ } } })// 看做父组件 new Vue({ el: '#app', data: { count: 11 } }) </script> </body> </html>
动态组件

多个组件可以使用同一个挂载点,动态切换

Component

<div id="app">
    <input type="button" value="切换到第1个组件" @click="tabComponent(1)">
    <input type="button" value="切换到第2个组件" @click="tabComponent(2)">
    <input type="button" value="切换到第3个组件" @click="tabComponent(3)">
    <component :is="current"></component>
</div>
<script>
​
​
    // 第一个组件
    const custom1 = Vue.component('custom1', {
        template: `
我是第1个组件
`
}) // 第二个组件 const custom2 = Vue.component('custom2', { template: `
我是第2个组件
`
}) // 第三个组件 const custom3 = Vue.component('custom3', { template: `
我是第3个组件
`
})new Vue({ el: '#app', data: { current: custom1 }, methods: { tabComponent(index) { if (index === 1) { this.current = custom1 } else if(index ===2) { this.current = custom2 } else { this.current = custom3 } } } }) </script>
keep-alive

包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们

它自身不会渲染一个 DOM 元素,也不会出现在父组件链中

<div id="app">
    <input type="button" value="切换到第1个组件" @click="tabComponent(1)">
    <input type="button" value="切换到第2个组件" @click="tabComponent(2)">
    <input type="button" value="切换到第3个组件" @click="tabComponent(3)">
    <keep-alive>
        <component :is="current"></component>
    </keep-alive>
</div>
<script>
​
​
    // 第一个组件
    const custom1 = Vue.component('custom1', {
        template: `
我是第1个组件
`
, methods: { changeBg(ev) { ev.target.style.background = 'orange' } } }) // 第二个组件 const custom2 = Vue.component('custom2', { template: `
我是第2个组件
`
}) // 第三个组件 const custom3 = Vue.component('custom3', { template: `
我是第3个组件
`
})new Vue({ el: '#app', data: { current: custom1 }, methods: { tabComponent(index) { if (index === 1) { this.current = custom1 } else if(index ===2) { this.current = custom2 } else { this.current = custom3 } } } }) </script>
扩展
  • include - 字符串或正则表达式。只有名称匹配的组件会被缓存。

  • exclude - 字符串或正则表达式。任何名称匹配的组件都不会被缓存。

  • max - 数字。最多可以缓存多少组件实例。

扩展 - 实战示例

第六天

Slot - 插槽

基础示例 - 匿名插槽

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="../vue.js"></script>
</head>
<body>
<div id="app">
    <custom>
        <p>我是HTML结构</p>
    </custom>
</div>
<script>
    Vue.component('custom', {
        template: `

hello,1902B

`
})new Vue({ el: '#app' }) </script> </body> </html>

进阶示例 - 具名插槽

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="../vue.js"></script>
</head>
<body>
<div id="app">
    <custom>
        <div slot="third">
            <ol>
                <li>111</li>
                <li>222</li>
                <li>333</li>
            </ol>
        </div><div slot="second">替换第二个</div><div slot="first">替换第三个</div>
    </custom>
</div>
<script>
    Vue.component('custom', {
        template: `

hello,1902B

`
})new Vue({ el: '#app' }) </script> </body> </html>

插槽作用域

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="../vue.js"></script>
</head>
<body>
<div id="app">
    <custom>
        {{ message }}
    </custom>
</div>
<script>
    
    // 看做子组件
    Vue.component('custom', {
        data() {
            return {
                message: '我是子组件的数据'
            }
        },
        template: `
{{ message }}

hello,1902B

`
})// 看做父组件 new Vue({ el: '#app', data: { message: '我是父组件的数据' } }) /* * 作用域,互不干扰 * */ </script> </body> </html>
生命周期

Vue2.0默认是8个生命周期
方法名 状态 含义
beforeCreate creating 状态 实例创建之前调用
beforeCreate creating 状态 实例创建之前调用
created creating 状态 实例创建成功,此时 data 中的数据显示出来了
beforeMount mounting 状态 数据中的 data 在模版中先占一个位置
mounted mounting 状态 模版中的 data 数据直接显示出来了
beforeUpdate updating 状态 当 data 数据发生变化调用,发生在虚拟 DOM 重新渲染和打补丁之前
updated updating 状态 数据更改导致的虚拟 DOM 重新渲染和打补丁
beforeDestroy destroying 状态 在 vue 实例销毁之前调用,此时实例任然可用
destroyed destroying 状态 在 vue 实例销毁之后调用,vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁

如果使用了keep-alive会是10个(activated、deactivated)

  • activated:来缓存组件状态,这个时候created钩子就不会被重复调用了, 如果我们的子组件需要在每次加载的时候进行某些操作,可以使用activated钩子触发

  • deactivated:组件移除时触发根据服务端还是客户端渲染机制,生命周期依旧会发生变化

  • updated 不被执行

  • destroyed 不被执行

生命周期就像搞对象
  • 哎呀,好无聊啊,看看人家都有对象,一起学习/一起看电影一起做爱做的事,真好。我也好想有个女朋友哦(beforeCreate,概念化阶段,什么都没有)。

  • 你要找的女朋友是什么样的呢?首先她是个女孩子,一个嘴巴、两条腿,等等总之就是女孩子默认的特征(created,有了雏型默认的数据有了)。

  • 我未来的女朋友要像林志玲一样、或者像我的同桌这样的就行,根据自己的喜好已经勾勒出未来女朋友的样子(beforeMount,虚拟的还没有具体的目标)。

  • 哇,隔壁班级新来的那位女孩子不错,她就是我喜欢的类型,我要把她追到手,让她做我女朋友(mounted,有了具体的目标形成了真实的DOM展示给了用户)。

  • 追的我好累啊还没追到手,不行我得调整下战术,不能每天找她玩了,我要好好学习Vue在积云我说第二没人敢说第一那种的,给她补课(beforeUpdate,战术已调整还未投入实战)

  • 去班级看看她有没有什么不会的,教教她(updated,新的战术已投入试用)

  • 这死孩子真麻烦,天天教我Vue,搞的我好像多么笨似的,我自己能搞定四哥说了,要培养自己的学习能力及解决问题的能力,真烦人(beforeDestroy,已经很烦这个小伙子了,准备拒绝他的好意)

  • 你又来干啥玩意儿,我自己能搞定,你给我滚,滚回你们班去,你除了Vue还会啥?四哥没告诉你吗,javascript才是最重要的,你给我滚,别来烦我(destroyed,微信拉黑&隔离灭了你丫的,销毁了!!!)


第七天

Vue-router
两种模式
  • hash(默认)

  • history

嵌套路由
关键字:children
// 一级路由
{
      path: '/about',
      name: 'about',
      component: About,
      children: [ // 二级路由
        {
          path: '',
          name: 'about',
          component: Study
        }
      ]
},
router-link组件常见配置
  • tag

  • class

重定向
path: '*',
  redirect: '/'
  redirect: {
    // path: '/'
    name: 'home'
}

路由元信息
meta:{}

过渡动效

// template
<transition name="fade">
    <router-view />
 </transition>// CSS
.fade-enter {
  opacity: 0;
}
.fade-enter-active {
  transition: 1s;
}
.fade-enter-to {
  opacity: 1;
}
路由传参
  • 第一步:在目标路由配置要传递的参数, 类似以function 传参中的形参
{
    path: '/user/:userId?', 
    component: User
},
  • 第二步:在目标路由的router-link上进行真实数据的传递,类似function中的实参
<router-link :to="'/user/' + item.id" v-for="item in userList">{{ item.userName }}</router-link>
  • 第三步:在目标路由对应的组件内通过this.$route接收传递过来的参数
let { userId } = this.$route.params
传参方式
  • 第一种:字符串拼接
// 传递
:to="'/user/' + item.id"
// 接收
this.$route.params.userId
  • 第二种:params
// 传递
<router-link :to="{name:'user',params:{userId:item.id}}" v-for="item in userList">{{ item.userName }}</router-link>
// 接收
this.$route.params.userId
  • 第三种:query
// 传递
<router-link :to="{path:'/user',query:{userId:item.id}}" v-for="item in userList">{{ item.userName }}</router-link>
// 接收
this.$route.query.userId

query VS params

  • 显示上

    • query
      • http://www.wyf.com?id=0707
    • params
      • http://www.wyf.com/0707
  • 用法上

    • query
      • 传递的时候
      {path:'/user',query:{userId:item.id}}
      
      • 接收传递过来的数据通过
      this.$route.query
      
      • 路由表中的path:
      '/user/:userId?'中的/:userId?可以省略
      
    • params
      • 传递的时候
      {name:'user',params:{userId:item.id}}
      
      • 接收传递过来的数据通过:
      this.$route.params
      
      • 路由表中的path:
      '/user/:userId?'中的/:userId?不可以省略
      
导航守卫

vue-router 提供的导航守卫主要用来通过跳转或取消的方式守卫导航。

类型
  • 全局的

    • beforeEach - 前置钩子,进入之前干什么
    • afterEach - 后置钩子,进去之后干啥玩意儿
  • 单个路由的

    • beforeEnter - 进入路由表中的某个路由前,干啥玩意儿
  • 组件

    • beforeRouteEnter - 进入某个组件前

    • beforeRouteUpdate - 组件复用的时候触发

    • beforeRouteLeave - 离开组件

路由懒加载

这里详细 - O(∩_∩)O哈哈~

作业 - cnode

第八天

Axios

特性
  • 支持node端和浏览器端

  • promise

  • 丰富的配置项

    • transformRequest

    • transformResponse

    • baseURL

    • 拦截器

  • 社区庞大

安装
npm i axios 
使用
npm
  // 哪个地方用得到就在哪个组件引入
  import Axios from 'axios'
CDN
常用方法
  • 基本用法
axios({
  method: 'post',
  url: '/user/12345',
  data: {
    firstName: 'Fred',
    lastName: 'Flintstone'
  }
});
  • get
axios.get('/user', {
    params: {
      ID: 12345
    }
})
  • post
xios.post('/user', {
    firstName: 'Fred',
    lastName: 'Flintstone'
})
  • All
    多个请求,全部成功了,才算成功;有一个失败了,就算失败了
// 第一个接口
function getCnode() {
    return Axios.get('https://cnodejs.org/api/v1/topics111');
}
// 第二个接口
function getMall() {
    return Axios.get('https://api.it120.cc/small4/shop/goods/category/all');
}
​
// 并发请求
Axios.all([getCnode(), getMall()])
    .then( (res1, res2) => {
    // 两个请求现在都执行完成
    console.log(res1)
    console.log(res2)
}))
    .catch(err => {
    console.log(err)
})
  • Race - 赛跑,比谁跑得快
    多个接口,只要有一个成功了,就算成功
// 第一个接口
function getCnode() {
    return Axios.get('https://cnodejs.org/api/v1/topics111');
}
// 第二个接口
function getMall() {
    return Axios.get('https://api.it120.cc/small4/shop/goods/category/all');
}
​
// 并发请求
Axios.race([getCnode(), getMall()])
    .then(( (res1, res2) => {
    // 两个请求现在都执行完成
    console.log(res1)
    console.log(res2)
}))
    .catch(err => {
    console.log(err)
})
  • Spread() - 处理并发请求的便于取值的函数
// 第一个接口
function getCnode() {
    return Axios.get('https://cnodejs.org/api/v1/topics111');
}
// 第二个接口
function getMall() {
    return Axios.get('https://api.it120.cc/small4/shop/goods/category/all');
}
​
// 并发请求
Axios.all([getCnode(), getMall()])
    .then(Axios.spread(function (res1, res2) {
    // 两个请求现在都执行完成
    console.log(res1)
    console.log(res2)
}))
    .catch(err => {
    console.log(err)
})
拦截器

拦截器,顾名思义,用来拦截什么东西的

在Axios中拦截器是用来拦截ajax请求的

分两组共有四个拦截点

  • request
    • 成功
    • 失败
  • response
    • 成功
    • 失败

实战案例 - loading动画

  // App.vue
  
  export default {
      data() {
          return {
              isLoading: true // 默认显示
          }
      },
      created () {
          // 添加响应拦截器
          Axios.interceptors.response.use( (response) => {
              // 对响应数据做点什么
              this.isLoading = false // 数据请求成功isloading改为false,loading隐藏掉
              return response;
          });
      }
  }

扩展知识
Axios - 实战案例讲解

Element UI

见官网

第九天 - Vuex ❤

什么是Vuex

官网:Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式

用来管理全局组件状态或管理多个组件共享状态

  • 专门为Vue应用程序开发的状态管理模式
    状态可以理解为数据,其实就是用来管理我们的数据的

  • 采用集中式存储管理应用的所有组件的状态
    在做一个webApp的时候的时候里面会牵扯到很多数据,这个时候我们可以集中式的管理这些数据

Vuex使用场景

  • 组件关系较为复杂且嵌套层级过深
  • 多个视图依赖于同一个状态
Vuex的使用

安装
npm i vuex
使用
  1. 创建store/index.js
/**
 * index.js Created by SmallFour on 2019/9/28/9:07
 */
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const store = new Vuex.Store({
  // 状态管理对象
  state: {
    count: 100
  }
})

export default store
  1. 在入口文件main.js引入且挂在到Vue实例上
import store from './store'
new Vue({
  el: '#app', // 挂载点
  router, // 路由对象挂载
  store,
  components: { App }, // 注册组件
  template: '' // 使用组件
})
  1. 在increment组件中通过计算属性computed获取状态
<template>
  <span>{{ num }}</span>
</template>
<script>
/**
 *increment.vue Created by SmallFour on 2019/9/28/8:57
 */
export default {
    computed: {
        num() {
            return this.$store.state.count
        }
    }
}
</script>
核心属性
Store

类似容器,包含应用的大部分状态
一个页面只能有一个store
状态存储只能是响应式的

new Vuex.Store({})

state - 定义状态
包含整个应用的大部分状态的对象

// Vuex
state: {
    count: 100
}
// 组件中
computed: {
    num() {
        return this.$store.state.count
    }
}

mutations - 改变状态
不能直接改变store中的状态,唯一途径就是通过commit提交mutations

// VUex
mutations: {
    addIncrement(state,payLoad) {
        state.count+=payLoad.n
    }
}
// 组件
increment() {
   this.$store.commit('addIncrement', 5)
},

Getters
有时候我们需要从 store 中的 state 中派生出一些状态

可以理解为计算属性

应用实例:购物车总价及总数计算

// Vuex
getters: {
    filterCount (state) {
      return state.count >= 120 ? 120 : state.count
    }
}
// 组件中
computed: {
    num1() {
        return this.$store.getters.filterCount
    }
}
actions

在mutations里面直接异步操作会发生数据混乱的情况

  • Action 提交的是 mutation,而不是直接变更状态
  • Action 可以包含任意异步操作
// Vuex
actions: {
    addAction(content) {
      setTimeout(() => {
        content.commit('addIncrement')
      }, 1000)
    }
}
// 组件中
increment() {
    this.$store.dispatch('addAction')
},
modules

项目太大的情况下,在Vuex中会导致有很多个state、mutations、actions、getters等,导致整个项目难以维护,所以就有了modules模块分割

// Vuex中
// 分割模块 - select
const selectModule = {
  state: {
    val: '我是默认的value'
  },
  mutations: {
    // 改变value值
    changeMut(state, payLoad) {
      state.val = payLoad
    },
  }
}
const store = new Vuex.Store({
  modules: {
    selectModule // 注入到Store的根模块中
  }
})

// 组件中
computed: {
    getVal() {
        return this.$store.state.selectModule.val // 模块分割后取值的时候要加上子模块的名字。注意:在改变状态的时候不必添加子模块名字
    }
}

禁止转载

1902专属 - 禁止转载

你可能感兴趣的:(Vue课堂笔记)