VUE

A、vue核心

一、vue基本使用

​ 1、引入vue.js

​ 2、创建vue对象

​ el:指定根element(选择器)

​ data:初始化数据(页面可以访问)

​ 3、双向数据绑定:v-model

​ 4、显示数据:{{}}

​ 5、理解vue的mvvm实现

​ mvvm:

​ model:模型,数据对象(data)

​ view:视图,模板页面

​ viewModel:视图模型(vue的实例)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

    <div id="app">
        <input type="text" v-model="message">
        <p>Hello {{message}}</p>
    </div>

    <script type="text/javascript" src="../js/vue.js"></script>
    <script type="text/javascript">
        //创建 vue 实例
        var app = new Vue({
            el: "#app", //element   选择器
            data: {     //数据(model)
                message: 'Hello Vue!'
            }
        })
    </script>

</body>
</html>
二、模板语法

1、模板的理解:动态的html页面,包含了一些JS语法代码、双大括号表达式、指令(以v-开头的自定义标签属性)

2、双大括号表达式

​ ● 语法:{{exp}}

​ ● 功能:向页面输出数据

​ ● 可以调用对象的方法

3、指令

​ ● 强制数据绑定

​ 功能:指定变化的属性

​ 完整写法:v-bind:xxx=‘yyy’ //yyy会作为表达式解析执行

​ 简洁写法: :xxx=‘yyy’

​ ● 绑定事件监听

​ 功能:绑定指定事件名的回调函数

​ 完整写法:v-on:click = ‘xxx’

​ 简洁写法:@click=‘xxx’

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

    <div id="app">
        <h2>1、双大括号表达式</h2>
        <p>{{msg}}</p>
        <p>{{msg.toUpperCase()}}</p>
        <p v-html="msg"></p>    <!--textContent-->
        <p v-text="msg"></p>    <!--innerHTML-->

        <h2>2、指令一:强制数据绑定</h2>
        <img src="imgUrl" alt=""/>
        <img v-bind:src="imgUrl" alt=""/>
        <img :src="imgUrl" alt=""/>

        <h2>3、指令二:绑定事件监听</h2>
        <button v-on:click="test">test1</button>
        <button @click="test2('abc')">test2</button>
    </div>
    <script type="text/javascript" src="../js/vue.js"></script>
    <script type="text/javascript">
        var vm = new Vue({
            el: '#app',
            data: {
                msg: '<a href="http://www.atguigu.com">I Will Back!</a>',
                imgUrl: "https://cn.vuejs.org/images/logo.png"
            },
            methods: {
                test(){
                    alert('hehe!')
                },
                test2(content){
                    alert(content)
                }
            }
        })
    </script>

</body>
</html>
三、计算属性与监视

​ ● 计算属性

​ ○ 在 computed 属性对象中定义计算属性的方法

​ ○ 在页面中使用{{方法名}}来显示计算的结果

​ ● 监视

​ ○ 通过通过 vm 对象的$watch()或 watch 配置来监视指定的属性

​ ○ 当属性变化时, 回调函数自动调用, 在函数内部进行计算

​ ● 计算属性高级

​ ○ 通过 getter/setter 实现对属性数据的显示和监视

​ ○ 计算属性存在缓存, 多次读取只执行一次 getter 计算

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

    <div id="demo">
        姓: <input type="text" placeholder="First Name" v-model="firstName"><br>
        名: <input type="text" placeholder="Last Name" v-model="lastName"><br>
        姓名 1(单向): <input type="text" placeholder="Full Name" v-model="fullName1"><br>
        姓名 2(单向): <input type="text" placeholder="Full Name" v-model="fullName2"><br>
        姓名 3(双向): <input type="text" placeholder="Full Name2" v-model="fullName3"><br>
    </div>

    <script type="text/javascript" src="../js/vue.js"></script>
    <script type="text/javascript">
        var vm = new Vue({
            el: '#demo',
            data: {
                firstName: 'Kobe',
                lastName: 'bryant',
                fullName2: 'Kobe bryant'
            },
            computed: {
                fullName1: function () {
                    return this.firstName + " " + this.lastName
                },
                fullName3: {
                    get: function () {
                        return this.firstName + " " + this.lastName
                    },
                    set: function (value) {
                        var names = value.split(' ')
                        this.firstName = names[0]
                        this.lastName = names[1]
                    }
                }
            },
            watch: {
                lastName: function (newVal, oldVal) {
                    this.fullName2 = this.firstName + ' ' + newVal
                }
            }
        })
        vm.$watch('firstName', function (val) {
            this.fullName2 = val + ' ' + this.lastName
        })
    </script>
</body>
</html>
四、class 与 style 绑定

1、理解

​ ● 在应用界面中, 某个(些)元素的样式是变化的

​ ● class/style 绑定就是专门用来实现动态样式效果的技术

2、class绑定

​ ● :class=‘xxx’

​ ● 表达式是字符串: ‘classA’

​ ● 表达式是对象: {classA:isA, classB: isB}

​ ● 表达式是对象: {classA:isA, classB: isB}

3、style绑定

​ ● :style="{ color: activeColor, fontSize: fontSize + ‘px’ }"

​ ● 其中 activeColor/fontSize 是 data 属性

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .classA {
            color: red;
        }
        .classB {
            background: blue;
        }
        .classC {
            font-size: 50px;
        }
    </style>
</head>
    <div id="demo">
        <h2>1. class 绑定: :class='xxx'</h2>
        <p class="classB" :class="a">表达式是字符串: 'classA'</p>
        <p :class="{classA: isA, classB: isB}">表达式是对象: {classA:isA, classB: isB}</p>
        <p :class="['classA', 'classC']"> 表达式是数组: ['classA', 'classB']</p>

        <h2>2. style 绑定</h2>
        <p :style="{color, fontSize}">style="{ color: activeColor, fontSize: fontSize +
            'px' }"
        </p>

        <button @click="update">更新</button>
    </div>
    <script type="text/javascript" src="../js/vue.js"></script>
    <script type="text/javascript">
        new Vue({
            el : '#demo',
            data : {
                a: 'classA',
                isA: true,
                isB: false,
                color: 'red',
                fontSize: '20px'
            },
            methods : {
                update () {
                    this.a = 'classC'
                    this.isA = false
                    this.isB = true
                    this.color = 'blue'
                    this.fontSize = '30px'
                }
            }
        })
    </script>

</body>
</html>
五、条件渲染

1、条件渲染指令

​ ● v-if 与 v-else

​ ● v-show

2、比较 v-if 与 v-show

​ ● 如果需要频繁切换 v-show 较好

​ ● 当条件不成立时, v-if 的所有子节点不会解析(项目中使用)

​ ● v-if 的条件如果为 false,那么元素不会出现,并且也不会占用位置,而 v-show 为false时,元素不会出现,

​ 但是会占用位置,相当于 display:none

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

    <div id="demo">
        <h2 v-if="ok">表白成功</h2>
        <h2 v-else>表白失败</h2>
        <h2 v-show="ok">求婚成功</h2>
        <h2 v-show="!ok">求婚失败</h2>
        <br>
        <button @click="ok=!ok">切换</button>
    </div>
    <script type="text/javascript" src="../js/vue.js"></script>
    <script type="text/javascript">
        var vm = new Vue({
            el: '#demo',
            data: {
                ok: false
            }
        })
    </script>

</body>
</html>
六、列表渲染

1、列表显示指令

​ ● 数组: v-for / index

​ ● 对象: v-for / key

2、列表的更新显示

​ ● 删除 item

​ ● 替换 item

3、 列表的高级处理

​ ● 列表过滤

​ ● 列表排序

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

    <div id="demo">
        <h2>测试: v-for 遍历数组</h2>
        <ul>
            <li v-for="(p, index) in persons" :key="index">
                {{index}}--{{p.name}}--{{p.age}}
                --
                <button @click="deleteItem(index)">删除</button>
                --
                <button @click="updateItem(index, {name:'Jok',age:15})">更新</button>
            </li>
        </ul>
        <h2>测试: v-for 遍历对象</h2>
        <ul>
            <li v-for="(value, key) in persons[0]">
                {{ key }} : {{ value }}
            </li>
        </ul>
    </div>
    <script type="text/javascript" src="../js/vue.js"></script>
    <script type="text/javascript">
        new Vue({
            el: '#demo',
            data: {
                persons: [
                    {id: 1, name: 'Tom', age: 13},
                    {id: 2, name: 'Jack', age: 12},
                    {id: 3, name: 'Bob', age: 14}
                ]
            },
            methods: {
                deleteItem(index) {
                    this.persons.splice(index, 1)
                },
                updateItem(index, p) {
    // this.persons[index] = p // 页面不会更新
                    this.persons.splice(index, 1, p)
                }
            }
        })
    </script>

</body>
</html>

​ 列表的搜索与排序

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

    <div id="demo">
        <input type="text" name="searchName" placeholder="搜索指定用户名"
               v-model="searchName">
        <ul>
            <li v-for="(p, index) in filterPerson" :key="index">
                {{index}}--{{p.name}}--{{p.age}}
            </li>
        </ul>
        <button @click="setOrderType(1)">年龄升序</button>
        <button @click="setOrderType(2)">年龄降序</button>
        <button @click="setOrderType(0)">原本顺序</button>
    </div>
    <script type="text/javascript" src="../js/vue.js"></script>
    <script type="text/javascript">
        new Vue({
            el: '#demo',
            data: {
                orderType: 0, //0 代表不排序, 1 为升序, 2 为降序
                searchName: '',
                persons: [
                    {id: 1, name: 'Tom', age: 13},
                    {id: 2, name: 'Jack', age: 12},
                    {id: 3, name: 'Bob', age: 17},
                    {id: 4, name: 'Cat', age: 14},
                    {id: 4, name: 'Mike', age: 14},
                    {id: 4, name: 'Monica', age: 16}
                ]
            },
            methods: {
                setOrderType (orderType) {
                    this.orderType = orderType
                }
            },
            computed: {
                filterPerson() {
                    let {orderType, searchName, persons} = this
    // 过滤
                    persons = persons.filter(p => p.name.indexOf(searchName)!=-1)
                    // 排序
                    if(orderType!==0) {
                        persons = persons.sort(function (p1, p2) {
                            if(orderType===1) {
                                return p1.age-p2.age
                            } else {
                                return p2.age-p1.age
                            }
                        })
                    }
                    return persons
                }
            }
        })
    </script>

</body>
</html>
七、事件处理

1、绑定监听

​ ● v-on:xxx=“fun”

​ ● @xxx=“fun”

​ ● @xxx=“fun(参数)”

​ ● 默认事件形参: event

​ ● 隐含属性对象: $event

2、事件修饰符

​ ● prevent : 阻止事件的默认行为 event.preventDefault()

​ ● stop : 停止事件冒泡 event.stopPropagation()

3、按键修饰符

​ ● keycode : 操作的是某个 keycode 值的键

​ ● keyName : 操作的某个按键名的键(少部分)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

    <div id="example">
        <h2>1. 绑定监听</h2>
        <button v-on:click="test1">Greet</button>
        <button @click="test1">Greet2</button>
        <button @click="test2($event, 'hello')">Greet3</button>

        <h2>2. 事件修饰符</h2>
        <!-- 阻止事件默认行为 -->
        <a href="http://www.baidu.com" @click.prevent="test3">百度一下</a>
        <br/>
        <br/>
        <!-- 停止事件冒泡 -->
        <div style="width: 200px;height: 200px;background: red" @click="test4">
            <div style="width: 100px;height: 100px;background: green"
                 @click.stop="test5"></div>
        </div>

        <h2>3. 按键修饰符</h2>
        <input @keyup.8="test6">
        <input @keyup.enter="test6">
    </div>
    <script type="text/javascript" src="../js/vue.js"></script>
    <script type="text/javascript">
        new Vue({
            el: '#example',
            data: {
                name: 'Vue.js'
            },
            methods: {
                test1 (event) {
                // 方法内 `this` 指向 vm
                // alert('Hello ' + this.name + '!')
                // `event` 是原生 DOM 事件
                    alert(event.target.innerHTML)
                },
                test2 (event, msg) {
                    alert(event.target.innerHTML + '---' + msg)
                },
                test3() {
                    alert('阻止事件的默认行为')
                },
                test4() {
                    alert('out')
                },
                test5() {
                    alert('inner')
                },
                test6(event) {
                    alert(event.keyCode + '---' + event.target.value)
                }
            }
        })
    </script>

</body>
</html>
八、表单输入绑定

1、使用 v-model 对表单数据自动收集

​ ● text/textarea

​ ● checkbox

​ ● radio

​ ● select

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

    <div id="demo">
        <form @submit.prevent="handleSubmit">
            <span>用户名: </span>
            <input type="text" v-model="user.username"><br>
            <span>密码: </span>
            <input type="password" v-model="user.pwd"><br>
            <span>性别: </span>
            <input type="radio" id="female" value="female" v-model="user.sex">
            <label for="female"></label>
            <input type="radio" id="male" value="male" v-model="user.sex">
            <label for="male"></label><br>
            <span>爱好: </span>
            <input type="checkbox" id="basket" value="basketball"
                   v-model="user.likes">
            <label for="basket">篮球</label>
            <input type="checkbox" id="foot" value="football"
                   v-model="user.likes">
            <label for="foot">足球</label>
            <input type="checkbox" id="pingpang" value="pingpang"
                   v-model="user.likes">
            <label for="pingpang">乒乓</label><br>
            <span>城市: </span>
            <select v-model="user.cityId">
                <option value="">未选择</option>
                <option v-for="city in allCitys" :value="city.id">
                    {{ city.name }}
                </option>
            </select><br>
            <span>介绍: </span>
            <textarea v-model="user.desc" rows="10"></textarea><br><br>
            <input type="submit" value="注册">
        </form>
    </div>
    <script type="text/javascript" src="../js/vue.js"></script>
    <script type="text/javascript">
        var vm = new Vue({
            el: '#demo',
            data: {
                user: {
                    username: '',
                    pwd: '',
                    sex: 'female',
                    likes: [],
                    cityId: '',
                    desc: '',
                },
                allCitys: [{id: 1, name: 'BJ'}, {id: 2, name: 'SZ'},{id: 4, name:
                        'SH'}],
            },
            methods: {
                handleSubmit (event) {
                    alert(JSON.stringify(this.user))
                }
            }
        })
        </script>

</body>
</html>
九、vue的生命周期

​ vue的生命周期有三个阶段:初始化、更新、死亡,每个阶段都有一些生命周期回调函数(钩子函数)

​ 生命周期方法:

​ beforeCreate:初始化之前调用,不是创建之前,调用这个方式时vm对象已经创建了

​ created:初始化之后调用

​ beforeMount:挂载之前调用

​ 挂载的意思:假如页面中有多个相同的标签需要将同一个数据显示,vm并不会一个一个进行赋值更新,而

​ 是将页面的模型先不发生改变,将相同的标签中的数据全部先读取到内存中,然后再从内存中将数据

​ 全部更新完,更新完以后再一次性批量显示到页面,这个过程就叫做挂载

​ mounted:挂载之后调用

​ 以上方法调用完成之后,生命周期的第一个阶段(初始化)就完成了

​ beforeUpdate:更新之前调用

​ updated:更新之后调用

​ 以上两个方法就是vue的更新阶段的生命周期方法

​ beforeDastroy:更新阶段之后,销毁vm之前调用

​ destroyed:销毁vm之后调用

​ 这两个方法是vue的死亡阶段的生命周期方法

在这里插入图片描述

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

    <div id="test">
        <button @click="destroyVM">destroy vm</button>
        <p v-show="isShow">尚硅谷IT教育</p>
    </div>

    <script type="text/javascript" src="../js/vue.js"></script>
    <script type="text/javascript">
        new Vue({
            el: '#test',
            data: {
                isShow: true
            },
            methods: {
                destroyVM(){
                    //干掉vm
                    this.$destroy()
                }
            },
            //初始化阶段
            beforeCreate(){
                console.log('beforeCreate')
            },
            created(){
                console.log('created')
            },
            beforeMount(){
                console.log('beforeMounted')
            },
            mounted() { //初始化显示之后立即调用(调用1次)
                this.intervalId = setInterval(() => {
                    this.isShow = !this.isShow
                }, 1000)
            },
            //初始化阶段
            beforeUpdate(){
                console.log('beforeUpdate')
            },
            updated(){
                console.log('updated')
            },
            //销毁阶段
            //销毁vm之前调用(调用1次)
            beforeDestroy(){
                //清除定时器
                clearInterval(this.intervalId)
            },
            destroyed(){
                console.log("destroyed")
            }
        })
    </script>

</body>
</html>
十、vue动画

1、vue动画的理解

​ 操作css的trasition或animation;vue会给目标元素添加/移除特定的class

2、基本过渡动画的编码

​ ● 在目标元素外包裹<transition name=“xxx”>

​ ● 定义class样式

​ ○ 指定过渡样式:transition

​ ○ 指定隐藏时的样式:opacity/其他

3、过渡的类名

​ ● xxx-enter-active:指定显示的transition

​ ● xxx-leave-active:指定隐藏的transition

​ ● xxx-enter:指定显示之前隐藏时的样式

​ ● .xxx-leave-to:指定显示之后隐藏时的样式

​ 注意:xxx和上面<transition name=“xxx”>中的xxx一样

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style type="text/css">
        /*显示隐藏的过渡效果*/
        .xxx-enter-active, .xxx-leave-active{
            transition: opacity 1s;
        }
        /*隐藏时的样式*/
        .xxx-enter, .xxx-leave-to{
            opacity: 0;
        }
        /*显示的过渡效果, 显示时间是1秒*/
        .yyy-enter-active {
            transition: all;
        }
        /*隐藏的过渡效果, 隐藏时间是3秒*/
        .yyy-leave-active {
            transition: all 3s;
        }
        /*隐藏时的样式*/
        .yyy-enter, .yyy-leave-to{
            opacity: 0;
            transform: translateX(20px);
        }
    </style>
</head>
<body>

    <div id="test">
        <button @click="isShow=!isShow">toggle</button>
        <transition name="xxx">
            <p v-show="isShow">hello</p>
        </transition>
    </div>

    <div id="test2">
        <button @click="isShow=!isShow">toggle</button>
        <transition name="yyy">
            <p v-show="isShow">hello</p>
        </transition>
    </div>
    
    <script type="text/javascript" src="../js/vue.js"></script>
    <script type="text/javascript">
        new Vue({
            el: '#test',
            data() {
                return {
                    isShow: true
                }
            }
        })

        new Vue({
            el: '#test2',
            data() {
                return {
                    isShow: true
                }
            }
        })
    </script>

</body>
</html>
十一、过滤器

1、理解

​ ● 功能:对要显示的数据进行特定格式化后再显示

​ ● 注意:并没有改变原本的数据,可是产生新的对应的数据

2、定义和使用过滤器

​ ● 定义过滤器

​ Vue.filter(filterName, function(value[,arg1, arg2, …]){

​ //进行一定的数据处理

​ return newValue;

​ })

​ ● 使用过滤器

​ <div>{{myData | filerName}}</div>

​ <div>{{myData | filterName(arg)}}</div>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

    <!--需求:对当前时间进行指定格式显示-->
    <div id="test">
        <h2>显示格式化的日期时间</h2>
        <p>{{date}}</p>
        <!--date表示要显示的数据,一般为vue实例中data中定义的数据,dateStr表示过滤器的名字-->
        <p>完整版:{{date | dateStr}}</p>
        <!--年月日-->
        <p>{{date | dateStr('YYYY-MM-DD')}}</p>
        <!--时分秒-->
        <p>{{date | dateStr('HH:mm:ss')}}</p>
    </div>
    <script type="text/javascript" src="../js/vue.js"></script>
    <script type="text/javascript" src="https://cdn.bootcss.com/moment.js/2.24.0/moment.js"></script>
    <script type="text/javascript">
        //自定义过滤器, value是将要进行格式化的数据
        Vue.filter('dateStr', function (value, format) {
            return moment(value).format(format || 'YYYY-MM-DD HH:mm:ss')
        })
        new Vue({
            el: '#test',
            data: {
                date: new Date()
            }
        })
    </script>

</body>
</html>
十二、vue指令

1、常用内置指令

​ ● v:text : 更新元素的 textContent

​ ● v-html : 更新元素的 innerHTML

​ ● v-if : 如果为 true, 当前标签才会输出到页面

​ ● v-else: 如果为 false, 当前标签才会输出到页面

​ ● v-show : 通过控制 display 样式来控制显示/隐藏

​ ● v-for : 遍历数组/对象

​ ● v-on : 绑定事件监听, 一般简写为@

​ ● v-bind : 强制绑定解析表达式, 可以省略 v-bind

​ ● v-model : 双向数据绑定

​ ● ref : 指定唯一标识, vue 对象通过$refs 属性访问这个元素对象

​ ● v-cloak : 防止闪现, 与 css 配合: [v-cloak] { display: none } 。解决问题:如果请求的数据很慢,那么在请求数

​ 据还没有到达页面时,页面会显示{{msg}}这样的符号,为了解决这个问题,给标签添加一个 v-cloak属性,

​ 然后利用属性选择器添加一个display: none 的样式,让数据没回来时不显示{{msg}}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style type="text/css">
        [v-cloak]{
            display: none;
        }
    </style>
</head>
<body>

    <div id="example">
        <p ref="content">atguigu.com</p>
        <button @click="hint">提示</button>
        <p v-cloak>{{msg}}</p>
    </div>
    <script type="text/javascript" src="../js/vue.js"></script>
    <script type="text/javascript">
        new Vue({
            el: '#example',
            data: {
                msg: '李鹏'
            },
            methods: {
                hint(){
                    alert(this.$refs.content.textContent)
                }
            }
        })
    </script>

</body>
</html>

2、自定义指令

​ ● 注册全局指令

​ Vue.directive(‘my-directive’, function(el, binding){

​ el.innerHTML = binding.value.toupperCase()

​ })

​ ● 注册局部指令

​ directives : {

​ ‘my-directive’ : {

​ bind (el, binding) {

​ el.innerHTML = binding.value.toupperCase()

​ }

​ }

​ }

​ ● 使用指令

​ v-my-directive='xxx

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

    <!--
        需求:自定义2个指令
            1、功能类似于v-text,但转换为全大写   v-upper-text
            2、功能类似于v-text,但转换为全小写   v-lower-text
    -->
    <div id="test1">
        <p v-upper-text="msg1"></p>
        <p v-lower-text="msg1"></p>
    </div>
    <div id="test2">
        <p v-upper-text="msg2"></p>
        <p v-lower-text="msg2"></p>
    </div>
    
    <script type="text/javascript" src="../js/vue.js"></script>
    <script type="text/javascript">
        //定义全局指令
        //el: 指令属性所在的标签对象
        //binding: 包含指令相关信息数据的对象
        Vue.directive('upper-text', function (el, binding) {
            el.textContent = binding.value.toUpperCase()
        })
        new Vue({
            el: '#test1',
            data: {
                msg1: 'NBA I Love This Game!'
            },
            directives: {  //注册局部指令, 只在当前vm管理的范围内有效
                'lower-text': function (el, binding) {
                    el.textContent = binding.value.toLowerCase()
                }
            }
        })
        new Vue({
            el: '#test2',
            data: {
                msg2: 'Just Do It!'
            }
        })
    </script>

</body>
</html>
十三、vue插件

1、首先参照官网定义插件,下面是一个简单的插件

/**
 * Created by 86130 on 2019/11/14.
 */

(function () {

    const MyPlugin = {};

    MyPlugin.install = function (Vue, options) {
        // 1. 添加全局方法或属性
        Vue.myGlobalMethod = function () {
            console.log('Vue函数对象的方法myGlobalMethod()')
        }

        // 2. 添加全局资源
        Vue.directive('my-directive', function (el, binding) {
            el.textContent = binding.value.toUpperCase()
        })

        // 3. 注入组件选项
        // Vue.mixin({
        //     created: function () {
        //         // 逻辑...
        //     }
        //
        // })

        // 4. 添加实例方法
        Vue.prototype.$myMethod = function (methodOptions) {
            // 逻辑...
            console.log('Vue实例对象的方法$myMethod()')
        }
    }
    //向外暴露
    window.MyPlugin = MyPlugin
})()

2、使用上上面定义的插件,使用插件之前,需要使用Vue.use(‘插件名’)来声明使用

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

    <div id="test">
        <p v-my-directive="msg"></p>
    </div>

    <script type="text/javascript" src="../js/vue.js"></script>
    <script type="text/javascript" src="./vue-myPlugin.js"></script>
    <script type="text/javascript">
        //声明使用插件
        Vue.use(MyPlugin)   //内部会执行MyPlugin.install(Vue)

        Vue.myGlobalMethod()

        const vm = new Vue({
            el: '#test',
            data: {
                msg: 'I Like You!'
            }
        })
        vm.$myMethod()
    </script>

</body>
</html>

B、vue组件化编码

一、使用vue-cli创建模板项目

1、说明

​ ● vue-cli 是 vue 官方提供的脚手架工具

​ ● 地址:github:https://github.com/vuejs/vue-cli 是一个js库

​ ● 作用:从 https://github.com/vuejs-templates 下载模板项目

2、创建vue项目

​ ● 全局安装脚手架:npm install -g vue-cli

​ 安装完成以后会多一个 vue 的命令,后面步骤会用上这个命令

​ ● 下载模板项:vue init webpack vue_demo

​ ○ webpack:模板中的一种,一共有6种模板,webpack只是其中的一种,意思相当于可以使用webpack打

​ 包;

​ ○ vue_demo:表示创建的项目的名字,可以自己定义

​ ○ 运行这条命令会有一些选项

​ ■ 第一个:因每个项目中都有一个package.json,这个选项是确认package.json中的name叫什么,注

​ 意这个name不能使用大写,可以使用下划线

​ ■ 第二个:确认描述,可以使用默认值

​ ■ 第三个:确认作者,可以使用默认值

​ ■ 第四个:是否使用vue-router(vue路由),先可以暂时不用,输入n即可

​ ■ 第五个:是否使用ESLint,可以选择y

​ ■ 第六个:是否使用单元测试的库,选择n

​ ■ 第七个:直接选择n

​ ■ 第八个:选择下载方式,可以是npm下载、yarn下载或手工下载,选择手工下载,后面的步骤参考

​ 下面的笔记

​ ● 进入自己创建的项目的目录里面:cd vue_demo

​ ● 安装:npm install

​ ● 运行:npm run dev

​ ● 访问:http://localhost:8080/

二、模板项目的结构

|-- build : webpack 相关的配置文件夹(基本不需要修改)

​ |-- dev-server.js : 通过 express 启动后台服务器

|-- config: webpack 相关的配置文件夹(基本不需要修改)

​ |-- index.js: 指定的后台服务的端口号和静态资源文件夹

|-- node_modules

|-- src : 源码文件夹

​ |-- components: vue 组件及其相关资源文件夹

​ |-- App.vue: 应用根主组件

​ |-- main.js: 应用入口 js,在build目录下webpack.base.conf.js中的module.exports→entry→app配置名称

|-- static: 静态资源文件夹

|-- .babelrc: babel 的配置文件,ES6转ES5的配置文件,rc:runtime control

|-- .eslintignore: eslint 检查忽略的配置,配置忽略eslint检查的文件

|-- .eslintrc.js: eslint 检查的配置

|-- .gitignore: git 版本管制忽略的配置

|-- index.html: 主页面文件

|-- package.json: 应用包配置文件

|-- README.md: 应用描述说明的 readme 文件

三、HelloWorld

1、在src下创建main.js作为主入口js文件

2、在src下创建App.vue作为根组件,它的结构基本固定。写完代码后可能会报错,可以把eslint、jslint、jshint都

​ 关闭,打开settings,搜索上面的三个关键字,把前面的enable勾去掉即可

3、在src下创建components目录,用于存放其他组件。由于组件的结构基本固定,我们可以创建一个模板,创建

​ 模板的步骤:打开settings→Editor→File and Code Templates→名字为vue,或者也是vue,不要加点,模

​ 板如下

<template>
  <div>

  </div>
</template>

<script>
  export default{
    
  }
</script>

<style>

</style>


4、在src/components目录下创建一个HelloWorld.vue,暴露的data必须是函数

<template>
    <div>
      <p class="msg">{{msg}}</p>
    </div>
</template>

<script>
    export default{ //配置对象(与vue一致)
      data() {  //必须写函数
          return {
            msg: 'Hello Vue Component'
          }
      }
    }
</script>

<style>
  .msg{
    color: red;
    font-size: 30px;
  }
</style>


5、在App.vue中引入组件、映射组件标签、使用组件标签,以及也可以将自己组件中的标签添加样式

<template>
  <div>
    <img class="logo" src="./assets/logo.png" alt="logo"/>
    <!--3、使用组件标签-->
    <HelloWorld/>
  </div>
</template>

<script>
  //1、引入组件
  import HelloWorld from './components/HelloWorld.vue'
  export default{
    //2、映射组件标签
    components: {
      HelloWorld
    }
  }
</script>

<style>
  .logo{
    width: 200px;
    height: 200px;
  }
</style>


6、在main.js主入口js文件中创建vue实例,首先要引入vue,但是格式只能是 import Vue from ‘vue’ 这种格式。

​ 其次引入根组件,使用components映射成标签。然后用template使用根组件标签

/*
*   创建Vue实例
* */
import Vue from 'vue'
import App from './App.vue'

new Vue({
  el: '#app',  //跟主页面中的模块id一致
  components: {
    App
  },
  template: '<App/>'
})

7、使用 npm run dev 运行,然后访问

四、项目的打包与发布

1、打包

​ 上面的方式没有生成打包文件,使用 npm run build 打包

2、发布

​ ● 使用静态服务器工具包

​ ○ npm install -g serve

​ ○ serve dist ----dist是第1步中打包生成的目录

​ ○ 访问:使用serve dist 命令后会打印出一个地址,我们通过这个地址访问。http://localhost:5000

​ ● 使用动态web服务器(tomcat)

​ ○ 修改 webpack.prod.conf.js

​ output: {

​ publicPath: ‘/xxx/’ //打包文件夹的名称,如:vue_demo

​ }

​ ○ 重新打包:npm run build

​ ○ 修改 dist 文件夹为项目名称: xxx

​ ○ 将 xxx 拷贝到运行的 tomcat 的 webapps 目录下

​ ○ 访问: http://localhost:8080/xxx

五、eslint编码规范检查

1、说明

​ ● ESList是一个代码规范检查工具

​ ● 它定义了很多特定的规则,一旦你的代码违背了某一规则,eslist会作出非常有用的提示

​ ● 官网:http://eslint.org/

​ ● 基本已替代以前的 JSList

2、ESLint提供以下支持

​ ● ES

​ ● JSX

​ ● style 检查

​ ● 自定义错误和提示

3、ESLint 提供以下几种校验

​ ● 语法错误校验

​ ● 不重要或丢失的标点符号, 如分号

​ ● 没法运行到的代码块(使用过 WebStorm 的童鞋应该了解)

​ ● 未被使用的参数提醒

​ ● 确保样式的统一规则, 如 sass 或者 less

​ ● 检查变量的命名

4、规则的错误等级有三种

​ ● 0 或 off: 关闭规则

​ ● 1: 打开规则, 并且作为一个警告(信息打印黄色字体)

​ ● 2: 打开规则, 并且作为一个错误(信息打印红色字体)

5、相关配置文件

​ ● .eslintrc.js : 全局规则配置文件

​ ‘rules’: {

​ ‘no-new’: 1

​ }

​ ● 在 js/vue 文件中修改局部规则

​ ● .eslintignore: 指令检查忽略的文件

​ *.js

​ *.vue

六、组件定义与使用
1、vue 文件的组成(3 个部分)

​ ● 模板页面

	<template>
		页面模板
	</template> 

​ ● JS 模块对象

	<script>
        export default {
            data() {return {}},
            methods: {},
            computed: {},
            components: {}
        }
	</script>

​ ● 样式

	<style>
        样式定义
	</style>

2、基本使用

​ ● 引入组件

​ ● 映射成标签

​ ● 使用组件标签

<template>
	//使用组件标记
	<HelloWorld></HelloWorld>
	<hello-world></hello-world>
</template>
<script>
    //引入组件
	import HelloWorld from './components/HelloWorld'
    //映射成标签
	export default {
		components: {
			HelloWorld
		}
	}
</script>

3、关于标签名与标签属性名书写问题

​ ● 写法一: 一模一样

​ ● 写法二:大写变小写, 并用-连接

七、组件间通信
1、组件间通信基本原则

​ ● 不要在子组件中直接修改父组件的状态数据

​ ● 数据在哪, 更新数据的行为(函数)就应该定义在哪

2、vue 组件间通信方式
A、props

​ ● 使用组件标签时

<my-component name='tom' :age='3' :set-name='setName'></my-component>

​ ● 定义 MyComponent 时

​ ○ 在组件内声明所有的 props

​ ■ 方式一: 只指定名称

		props: ['name', 'age', 'setName']

​ ■ 方式二: 指定名称和类型

		props: {
            name: String,
            age: Number,
            setNmae: Function
        }

​ ■ 方式三: 指定名称/类型/必要性/默认值

		props: {
        	name: {type: String, required: true, default:xxx},
        }

​ ● 注意

​ ○ 此方式用于父组件向子组件传递数据

​ ○ 所有标签属性都会成为组件对象的属性, 模板页面可以直接引用

​ ○ 问题:

​ ■ 如果需要向非子后代传递数据必须多层逐层传递

​ ■ 兄弟组件间也不能直接 props 通信, 必须借助父组件才可以

B、vue 的自定义事件

​ ● 绑定事件监听

// 方式一: 通过 v-on 绑定, 在父组件中使用子组件的标签上添加下面的属性
@delete_todo="deleteTodo"
// 方式二: 通过$on()
this.$refs.xxx.$on('delete_todo', function (todo) {
	this.deleteTodo(todo)
})

​ ● 触发事件

// 触发事件(只能在父组件中接收), 在子组件中触发上面绑定的监听函数
this.$emit(eventName, data)

​ ● 注意:

​ ○ 此方式只用于子组件向父组件发送消息(数据)

​ ○ 问题: 隔代组件或兄弟组件间通信此种方式不合适

C、消息订阅与发布(PubSubJS 库)

​ ● 下载安装 pubsubjs库

​ npm install --save pubsub-js

​ ● 订阅消息

	import PubSub from 'pubsub-js'	//哪个组件使用就从哪个组件导入
	PubSub.subscribe('msg111', function(msg, data){})

​ ● 发布消息

	PubSub.publish('msg111', data)

​ ● 注意

​ 优点: 此方式可实现任意关系组件间通信(数据)

​ ● 事件的 2 个重要操作(总结)

​ ○ 绑定事件监听 (订阅消息)

​ 目标: 标签元素

​ 事件名(类型): click/focus

​ 回调函数: function(event){}
​ ○ 触发事件 (发布消息)

​ DOM 事件: 用户在浏览器上对应的界面上做对应的操作

​ 自定义: 编码手动触发

D、slot

​ ● 理解

​ 此方式用于父组件向子组件传递`标签数据`

​ ● 子组件: Child.vue

<template>
    <div>
        <slot name="xxx">不确定的标签结构 1</slot>
        <div>组件确定的标签结构</div>
        <slot name="yyy">不确定的标签结构 2</slot>
    </div>
</template>

​ ● 父组件: Parent.vue

​ 注意:这种方式所有关于子组件中的代码都需要放入到父组件中编写

<child>
    <div slot="xxx">xxx 对应的标签结构</div>
    <div slot="yyy">yyyy 对应的标签结构</div>
</child>

E、vuex(后面单独讲)
八、鼠标移入移出事件区别

​ 鼠标移入元素有两个事件:onmouseenter、onmouseover

​ 鼠标移出元素有两个事件:onmouseleave、onmouseout

​ 他们的区别:当有一个大div中包含一个小div时,当鼠标移入大div时都会触发 onmouseenter、onmouseover

​ ,当从大div中移入小div时,会触发大div的 onmouseout;当从小div中移出到大div时会触发onmouseover,

​ 移出大div时都会触发onmouseleave、onmouseout

九、存储数据

​ 数据存储,以便浏览器关闭后,下次再打开时还能看到之前的数据,使用 window.localStorage 进行数据存储

​ 获取数据:

window.localStorage.getItem('tasks_key')

​ 设置数据:

window.localStorage.setItem('tasks_key', value)

C、vue_ajax

一、vue 项目中常用的 2 个 ajax 库

1、vue-resource

​ vue 插件, 非官方库, vue1.x 使用广泛

2、axios

​ 通用的 ajax 请求库, 官方推荐, vue2.x 使用广泛

二、vue-resource 的使用

1、在线文档

​ https://github.com/pagekit/vue-resource/blob/develop/docs/http.md

2、下载

​ npm install vue-resource --save

3、下载后,在main.js(主入口js文件)中引入vue-resource,然后在main.js中声明使用插件。声明以后,内部会给

​ vm对象和组件对象添加一个属性:$http,它里面有两个方法:get()和post()

	import VueResource from 'vue-resource'
	//声明使用插件
	Vue.use(VueResource)

4、编码,请求一般放在 mounted() 声明周期方法中

    // 引入模块
    import VueResource from 'vue-resource'
    // 使用插件
    Vue.use(VueResource)
    // 通过 vue/组件对象发送 ajax 请求
    this.$http.get(url).then((response) => {
            // success callback
            console.log(response.data) //返回结果数据
        }, (response) => {
            // error callback
            console.log(response.statusText) //错误信息
    	})

三、axios 的使用

1、axios 的使用

2、在线文档

​ https://github.com/pagekit/vue-resource/blob/develop/docs/http.md

3、下载

​ npm install axios --save

4、编码

​ 导入 axios 时,哪个组件使用就从哪个组件中导入

    // 引入模块
    import axios from 'axios'
    // 发送 ajax 请求
    axios.get(url)
        .then(response => {
        	console.log(response.data) // 得到返回结果数据
        })
        .catch(error => {
        	console.log(error.message)
   	 	})

D、VUE UI组件库

一、常用

​ ● Mint UI

​ ○ 主页:http://mint-ui.github.io/#!/zh-cn

​ ○ 说明:饿了么开源的基于 vue 的移动端 UI 组件库

​ ● Elment

​ ○ 主页:http://element-cn.eleme.io/#/zh-CN

​ ○ 说明:饿了么开源的基于 vue 的 PC 端 UI 组件库

二、使用Mint UI

1、安装

​ npm install --save mint-ui

2、实现按需打包

1. 下载
	npm install --save-dev babel-plugin-component
2. 修改 babel 配置
    "plugins": ["transform-runtime",["component", [
        {
            "libraryName": "mint-ui",
            "style": true
        }
    ]]]

3、mint-ui 组件分类

​ ● 标签组件

​ ● 非标签组件

4、使用 mint-ui 的组件

​ ● index.html

<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1,
minimum-scale=1, user-scalable=no" />
<!-- 引入实现快速点击的库 -->
<script src="https://as.alipayobjects.com/g/component/fastclick/1.0.6/fastclick.js"></script>
<script>
    if ('addEventListener' in document) {
        document.addEventListener('DOMContentLoaded', function() {
        	FastClick.attach(document.body);
        }, false);
    } 
    if(!window.Promise) {
        document.writeln('<script
        src="https://as.alipayobjects.com/g/component/es6-promise/3.2.2/es6-promise.min.js"
        '+'>'+'<'+'/'+'script>');
    }
</script>

​ ● main.js

import {Button} from 'mint-ui'
Vue.component(Button.name, Button)

​ ● App.vue

<template>
	<mt-button @click="handleClick" type="primary" style="width: 100%">Test</mt-button>
</template>
<script>
    import {Toast} from 'mint-ui'
    export default {
        methods: {
            handleClick () {
            	Toast('点击了测试');
        	}
    	}
	}
</script>

E、vue-router

一、理解

1、说明

​ ● 官方提供的用来实现 SPA 的 vue 插件

​ ● github: https://github.com/vuejs/vue-router

​ ● 中文文档: http://router.vuejs.org/zh-cn/

​ ● 下载: npm install vue-router --save

2、相关 API 说明

​ ● VueRouter(): 用于创建路由器的构建函数

    new VueRouter({
    	// 多个配置项
    })

​ ● 路由配置

    routes: [
        { // 一般路由
            path: '/about',
            component: About
        },
        { // 自动跳转路由
            path: '/',
            redirect: '/about'
        }
    ]

​ ● 注册路由器

    import router from './router'
    new Vue({
    	router
    })

​ ● 使用路由组件标签

​ ○ : 用来生成路由链接

​ Go to XXX

​ ○ : 用来显示当前路由组件界面

二、基本路由

1、在src下面创建App.vue、main.js以及两个文件夹,一个文件夹放子组件,一般我们命名为 views或pages,一

​ 个文件夹用于存放路由器的配置文件,命名为 router,路由器配置文件命名为 index.js

2、在主入口main.js中引入router

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

new Vue({ //配置对象的属性名都是一些确定的名称,不能随便修改
  el: '#app',
  components: {App},
  template: '<App/>',
  router
})


3、在router目录下的index.js配置路由,需要引入vue-router,然后声明使用,暴露需要用到的路由,注意:这里

​ 有一个默认的路由,使用redirect,当鼠标没有点击时会出现默认的路由,

/*
*   路由器模块
* */

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

import About from '../views/About.vue'
import Home from '../views/Home.vue'

Vue.use(VueRouter)

export default new VueRouter({
  //n个路由
  routes: [
    {
      path: '/about',
      component: About
    },
    {
      path: '/home',
      component: Home
    },
    {
      path: '/',
      redirect: '/about'
    }
  ]
})

4、在主组件App.vue中使用路由连接以及渲染当前路由组件

<!--路由链接-->
<router-link to="/about" class="routerLink">About</router-link>
<!--用于渲染当前路由组件-->
<router-view></router-view>

5、可以使用 .router-link-active 这个样式改变当前选择的路由,这个样式在选择路由的时候会默认加进去。

​ 除了 .router-link-active 这个样式以外,还可以使用 linkActiveClass: ‘active’,//指定选择的路由连接的class

.router-link-active{
    color: red !important;
}

三、嵌套路由

​ 嵌套路由和前面的基本路由差不多,只是要在配置路由的index.js中配置子路由

/**
 * Created by 86130 on 2019/11/17.
 */

/*
*   路由器模块
* */

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

import About from '../views/About.vue'
import Home from '../views/Home.vue'
import News from '../views/News.vue'
import Messages from '../views/Messages.vue'
import Title from '../views/Title.vue'

Vue.use(VueRouter)

export default new VueRouter({
  //n个路由
  routes: [
    {
      path: '/about',
      component: About
    },
    {
      path: '/home',
      component: Home,
      //配置嵌套路由
      children: [
        {
          path: '/home/news', //path前面的斜杠永远代表根路径
          component: News
        },
        {
          path: 'messages',
          component: Messages,
          children: [
            {
              path: 'title',
              component: Title
            }
          ]
        },
        {
          path: '',
          component: News
        }
      ]
    },
    {
      path: '/',
      redirect: '/about'
    }
  ]
})

四、缓存路由组件对象

1、理解

​ ● 默认情况下, 被切换的路由组件对象会死亡释放, 再次回来时是重新创建的

​ ● 如果可以缓存路由组件对象, 可以提高用户体验

2、编码

<keep-alive>
	<router-view></router-view>
</keep-alive>

五、向路由组件传递数据

方式 1: 路由路径携带参数(param/query)

1、配置路由

children: [
    {
    	path: 'mdetail/:id',
    	component: MessageDetail
    }
] 

2、路由路径

<router-link :to="'/home/message/mdetail/'+m.id">{{m.title}}</router-link> 
或者
<router-link :to="`/home/message/mdetail/${m.id}`">{{m.title}}</router-link>
如果使用query的方式,那么使用路由路径如下
<router-link :to="`/home/message/mdetail?id=${m.id}`">{{m.title}}</router-link>

3、路由组件中读取请求参数

​ this. r o u t e . p a r a m s . i d 或 者 t h i s . route.params.id 或者 this. route.params.idthis.route.query.id

方式 2: 属性携带数据

使用组件标签上面传递数据,方式和vue的props组件传递数据一致
<router-view :msg="msg"></router-view>
在路由组件使用props接收数据

六、编程式路由导航

1、相关 API

​ ● this.$router.push(path): 相当于点击路由链接**(可以返回到当前路由界面)**

​ ● this.$router.replace(path): 用新路由替换当前路由**(不可以返回到当前路由界面)**

​ ● this.$router.back(): 请求(返回)上一个记录路由

​ ● this.$router.go(-1): 请求(返回)上一个记录路由

​ ● this.$router.go(1): 请求下一个记录路由

2、编码

<template>
    <div>
      <ul>
        <li v-for="message in messages" :key="message.id">
          <router-link :to="`/home/messages/title/${message.id}`">{{message.title}}</router-link>
          <!-- 点击下面两个按钮调用$router的方法 -->
          <button @click="pushShow(message.id)">push查看</button>
          <button @click="replaceShow(message.id)">replace查看</button>
        </li>
      </ul>
      <button @click="$router.back()">回退</button>
      <div>
        <router-view :messages="messages"></router-view>
      </div>
    </div>
</template>

<script>
    export default{
      data() {
        return {
          messages: []
        }
      },
      methods: {
        pushShow(id){
//          this.$router.push(`/home/messages/title/${id}`)
          this.$router.push(`/login`)
        },
        replaceShow(id){
          this.$router.replace(`/home/messages/title/${id}`)
        }
      },
      mounted(){
        setTimeout(() => {
          const messages = [
            {
              id: 1,
              title: 'message001'
            },
            {
              id: 2,
              title: 'message002'
            },
            {
              id: 3,
              title: 'message003'
            }
          ]
          this.messages = messages
        }, 2000)
      }
    }
</script>

<style>

</style>


F、vue 源码分析

一、说明

1、分析 vue 作为一个 MVVM 框架的基本实现原理包含

​ ● 数据代理

​ ● 模板解析

​ ● 数据绑定

2、不直接看 vue.js 的源码

3、剖析 github 上某基友仿 vue 实现的 mvvm 库

4、地址: https://github.com/DMQ/mvvm

二、准备知识

​ ● [].slice.call(lis): 将伪数组转换为真数组

​ ● node.nodeType: 得到节点类型

​ ● Object.defineProperty(obj, propName, {}): 给对象添加/修改属性(指定描述符)

​ ○ configurable: true/false 是否可以重新 define

​ ○ enumerable: true/false 是否可以枚举(for…in / keys())

​ ○ value: 指定初始值

​ ○ writable: true/false value 是否可以修改

​ ○ get: 回调函数, 用来得到当前属性值

​ ○ set: 回调函数, 用来监视当前属性值的变化

​ ● Object.keys(obj): 得到对象自身可枚举的属性名的数组

​ ● DocumentFragment: 文档碎片(高效批量更新多个节点)

​ ● obj.hasOwnProperty(prop): 判断 prop 是否是 obj 自身的属性

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>

<div id="test">尚硅谷IT教育</div>

<ul id="fragment_test">
  <li>test1</li>
  <li>test2</li>
  <li>test3</li>
</ul>


<!--
1. [].slice.call(lis): 将伪数组转换为真数组
2. node.nodeType: 得到节点类型
3. Object.defineProperty(obj, propertyName, {}): 给对象添加属性(指定描述符)
4. Object.keys(obj): 得到对象自身可枚举属性组成的数组
5. obj.hasOwnProperty(prop): 判断prop是否是obj自身的属性
6. DocumentFragment: 文档碎片(高效批量更新多个节点)
-->

<script type="text/javascript">
  //1. [].slice.call(lis): 根据伪数组生成对应的真数组
  const lis = document.getElementsByTagName('li') // lis是伪数组(是一个特别的对象, length和数值下标属性)
  console.log(lis instanceof Object, lis instanceof Array)
  // 数组的slice()截取数组中指定部分的元素, 生成一个新的数组  [1, 3, 5, 7, 9], slice(0, 3)
  // slice2()
  Array.prototype.slice2 = function (start, end) {
    start = start || 0
    end = start || this.length
    const arr = []
    for (var i = start; i < end; i++) {
      arr.push(this[i])
    }
    return arr
  }
  const lis2 = Array.prototype.slice.call(lis)  // lis.slice()
  console.log(lis2 instanceof Object, lis2 instanceof Array)
  // lis2.forEach()

  //2. node.nodeType: 得到节点类型
  const elementNode = document.getElementById('test')
  const attrNode = elementNode.getAttributeNode('id')
  const textNode = elementNode.firstChild
  console.log(elementNode.nodeType, attrNode.nodeType, textNode.nodeType)

  //3. Object.defineProperty(obj, propertyName, {}): 给对象添加属性(指定描述符)
  const obj = {
    firstName: 'A',
    lastName: 'B'
  }
  //obj.fullName = 'A-B'
  Object.defineProperty(obj, 'fullName', {
    // 属性描述符:

    // 数据描述符

    //访问描述符
    // 当读取对象此属性值时自动调用, 将函数返回的值作为属性值, this为obj
    get () {
      return this.firstName + "-" + this.lastName
    },
    // 当修改了对象的当前属性值时自动调用, 监视当前属性值的变化, 修改相关的属性, this为obj
    set (value) {
      const names = value.split('-')
      this.firstName = names[0]
      this.lastName = names[1]
    }
  })

  console.log(obj.fullName) // A-B
  obj.fullName = 'C-D'
  console.log(obj.firstName, obj.lastName) // C D

  Object.defineProperty(obj, 'fullName2', {
    configurable: false, //是否可以重新define
    enumerable: true, // 是否可以枚举(for..in / keys())
    value: 'A-B', // 指定初始值
    writable: false // value是否可以修改
  })
  console.log(obj.fullName2)  // A-B
  obj.fullName2 = 'E-F'
  console.log(obj.fullName2) // A-B

  /*Object.defineProperty(obj, 'fullName2', {
    configurable: true,
    enumerable: true,
    value: 'G-H',
    writable: true
  })*/


  //4. Object.keys(obj): 得到对象自身可枚举属性组成的数组
  const names = Object.keys(obj)
  console.log(names)


  //5. obj.hasOwnProperty(prop): 判断prop是否是obj自身的属性
  console.log(obj.hasOwnProperty('fullName'), obj.hasOwnProperty('toString'))  // true false

  //6. DocumentFragment: 文档碎片(高效批量更新多个节点)
  // document: 对应显示的页面, 包含n个elment  一旦更新document内部的某个元素界面更新
  // documentFragment: 内存中保存n个element的容器对象(不与界面关联), 如果更新framgnet中的某个element, 界面不变
  /*
  <ul id="fragment_test">
    <li>test1</li>
    <li>test2</li>
    <li>test3</li>
  </ul>
   */
  const ul = document.getElementById('fragment_test')
  // 1. 创建fragment
  const fragment = document.createDocumentFragment()
  // 2. 取出ul中所有子节点取出保存到fragment
  let child
  while(child=ul.firstChild) { // 一个节点只能有一个父亲, 如果节点添加到另外一个父节点,那么子节点会从前一个父节点中删除
    fragment.appendChild(child)  // 先将child从ul中移除, 添加为fragment子节点
  }

  // 3. 更新fragment中所有li的文本
  Array.prototype.slice.call(fragment.childNodes).forEach(node => {
    if (node.nodeType===1) { // 元素节点 <li>
      node.textContent = 'atguigu'
    }
  })

  // 4. 将fragment插入ul
  ul.appendChild(fragment)

</script>
</body>
</html>

三、数据代理

​ ● 数据代理: 通过一个对象代理对另一个对象(在前一个对象内部)中属性的操作(读/写)

​ ● vue 数据代理: 通过 vm 对象来代理 data 对象中所有属性的操作

​ ● 好处: 更方便的操作 data 中的数据

​ ● 基本实现流程

​ ○ 通过 Object.defineProperty()给 vm 添加与 data 对象的属性对应的属性描述符

​ ○ 所有添加的属性都包含 getter/setter

​ ○ getter/setter 内部去操作 data 中对应的属性数据

四、模板解析

1、模板解析的基本流程

​ ● 将 el 的所有子节点取出, 添加到一个新建的文档 fragment 对象中

​ ● 对 fragment 中的所有层次子节点递归进行编译解析处理

​ ○ 对大括号表达式文本节点进行解析

​ ○ 对元素节点的指令属性进行解析

​ ■ 事件指令解析

​ ■ 一般指令解析

​ ● 将解析后的 fragment 添加到 el 中显示

2、模板解析(1): 大括号表达式解析

​ ● 根据正则对象得到匹配出的表达式字符串: 子匹配/RegExp.$1 name

​ ● 从 data 中取出表达式对应的属性值

​ ● 将属性值设置为文本节点的 textContent

3、模板解析(2): 事件指令解析

​ ● 从指令名中取出事件名

​ ● 根据指令的值(表达式)从 methods 中得到对应的事件处理函数对象

​ ● 给当前元素节点绑定指定事件名和回调函数的 dom 事件监听

​ ● 指令解析完后, 移除此指令属性

4、模板解析(3): 一般指令解析

​ ● 得到指令名和指令值(表达式) text/html/class msg/myClass

​ ● 从 data 中根据表达式得到对应的值

​ ● 根据指令名确定需要操作元素节点的什么属性

​ ○ v-text—textContent 属性

​ ○ v-html—innerHTML 属性

​ ○ v-class–className 属性
​ ● 将得到的表达式的值设置到对应的属性上

​ ●移除元素的指令属性

G、vuex

一、vuex理解

1、vuex 是什么

​ ● github 站点: https://github.com/vuejs/vuex

​ ● 在线文档: https://vuex.vuejs.org/zh-cn/

​ ● 简单来说: 对 vue 应用中多个组件的共享状态进行集中式的管理(读/写)

​ ● 安装 vuex:npm install --save vuex

2、状态自管理应用

​ ● state: 驱动应用的数据源

​ ● view: 以声明方式将 state 映射到视图

​ ● actions: 响应在 view 上的用户输入导致的状态变化(包含 n 个更新状态的方法)

3、多组件共享状态的问题

​ ● 多个视图依赖于同一状态

​ ● 来自不同视图的行为需要变更同一状态

​ ● 以前的解决办法

​ ○ 将数据以及操作数据的行为都定义在父组件

​ ○ 将数据以及操作数据的行为传递给需要的各个子组件(有可能需要多级传递)

​ ● vuex 就是用来解决这个问题的

二、vuex 核心概念和 API

1、state

​ ● vuex 管理的状态对象

​ ● 它应该是唯一的

​ const state = {

​ xxx: initValue

​ }

2、mutations

​ ● 包含多个直接更新 state 的方法(回调函数)的对象

​ ● 谁来触发: action 中的 commit(‘mutation 名称’)

​ ● 只能包含同步的代码, 不能写异步代码

​ ● const mutations = {

​ yyy (state, {data1}) {

​ // 更新 state 的某个属性

​ }

​ }

3、actions

​ ● 包含多个事件回调函数的对象

​ ● 通过执行: commit()来触发 mutation 的调用, 间接更新 state

​ ● 谁来触发: 组件中: $store.dispatch(‘action 名称’, data1) // ‘zzz’

​ ● 可以包含异步代码(定时器, ajax)

​ ● const actions = {

​ zzz ({commit, state}, data1) {

​ commit(‘yyy’, {data1})

​ }

​ }

4、getters

​ ● 包含多个计算属性(get)的对象

​ ● 谁来读取: 组件中: $store.getters.xxx

​ ● const getters = {

​ mmm (state) {

​ return …

​ }

​ }

5、modules

​ ● 包含多个 module

​ ● 一个 module 是一个 store 的配置对象

​ ● 与一个组件(包含有共享数据)对应

6、向外暴露 store 对象

​ export default new Vuex.Store({

​ state,

​ mutations,

​ actions,

​ getters

​ })

7、组件中

import {mapState, mapGetters, mapActions} from 'vuex'
export default {
    computed: {
    	...mapState(['xxx']),
    	...mapGetters(['mmm']),
    } 
	methods: mapActions(['zzz'])
} 
{{xxx}} {{mmm}} @click="zzz(data)"

8、映射 store

import store from './store'
new Vue({
	store
})

9、store 对象

​ ● 所有用 vuex 管理的组件中都多了一个属性$store, 它就是一个 store 对象

​ ● 属性:

​ ○ state: 注册的 state 对象

​ ○ getters: 注册的 getters 对象

​ ● 方法:

​ dispatch(actionName, data): 分发调用 action

Vuex详解:

​ vuex 结构图:

在这里插入图片描述

使用步骤:

方式1:

​ 1、安装 vuex,在控制台输入:

	npm install --save vuex

​ 2、创建 vuex 的核心管理对象模块:store。创建一个 store.js文件,存放在 src 目录下面,如:

在这里插入图片描述

​ 3、store.js 文件中内容如下,在暴露的对象中引入4个具体的对象,state、mutations、actions、getters:

/*
vuex最核心的管理对象store
 */
import Vue from 'vue'	//vuex是vue的插件,vue必须引入
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({	//Store是构造函数,存在Vuex对象里面的对象
  state,	//状态对象,用来存放具体数据
  mutations,	//包含多个更新state函数的对象
  actions,		//包含多个对应事件回调函数的对象
  getters		//包含多个getter计算属性函数的对象
})

​ 4、创建上面4个具体的对象,同样也是创建在 store.js 文件中,如下:

/*
vuex最核心的管理对象store
 */
import Vue from 'vue'	//vuex是vue的插件,vue必须引入
import Vuex from 'vuex'

Vue.use(Vuex)

//状态对象,用来存放具体数据
const state = {
    
}

//包含多个更新state函数的对象
const mutations = {
    
}

//包含多个对应事件回调函数的对象
const actions = {
    
}

//包含多个getter计算属性函数的对象
const getters = {
    
}

export default new Vuex.Store({	//Store是构造函数,存在Vuex对象里面的对象
  state,	//状态对象,用来存放具体数据
  mutations,	//包含多个更新state函数的对象
  actions,		//包含多个对应事件回调函数的对象
  getters		//包含多个getter计算属性函数的对象
})

​ 5、将 store 对象引入到入口 main.js 文件,如下:

/*
入口js
 */
import Vue from 'vue'
import Counter from './Counter.vue'
//引入 store 对象
import store from './store'

new Vue({
  el: '#app',
  components: { App },
  template: '<App/>',
  store // 注册上vuex的store: 所有组件对象都多一个属性$store
})

​ 6、将组件的 data 里面的数据放到 state 对象里面进行初始化管理,并且移出组件中 data 里面的数据,但是

​ 移出组件中 data 里面的数据后,怎样访问初始化的数据呢?

​ 由于所有用 vuex 管理的组件中都多了一个属性$store, 它就是一个 store 对象,这个对象中有两个属性:

​ state:注册的 state 对象

​ getters:注册的 getters 对象

​ 我们可以用 store 中的这两个属性访问数据,除了两个属性以外,还有一个方法:

​ dispatch(actionName, data):分发调用 action

​ 这个方法可以调用 actions 中的方法

/*
vuex最核心的管理对象store
 */
import Vue from 'vue'	//vuex是vue的插件,vue必须引入
import Vuex from 'vuex'

Vue.use(Vuex)

//状态对象,用来存放具体数据
const state = {	//初始化组件中的数据
    counter: 0
}

//包含多个更新state函数的对象
const mutations = {
    
}

//包含多个对应事件回调函数的对象
const actions = {
    
}

//包含多个getter计算属性函数的对象
const getters = {
    
}

export default new Vuex.Store({	//Store是构造函数,存在Vuex对象里面的对象
  state,	//状态对象,用来存放具体数据
  mutations,	//包含多个更新state函数的对象
  actions,		//包含多个对应事件回调函数的对象
  getters		//包含多个getter计算属性函数的对象
})

​ 7、在组件中引用数据,在标签中直接使用 $store.state.属性即可获取数据,但是用 js 编写的时候需要在前面

​ 加 this

<template>
	<div>
        <p>
            click {{$store.state.counter}}
        </p>
    </div>
</template>

​ 8、在组件中使用计算属性

​ 1)、定义计算属性。

​ 在 store.js 的 getters 中定义一个计算属性,注意,计算属性中引用 data 中的数据时,可以通过

​ state 这个对象访问数据,获取 state 时不需要其他操作,只要在计算属性方法的入参传入 state 即可

/*
vuex最核心的管理对象store
 */
import Vue from 'vue'	//vuex是vue的插件,vue必须引入
import Vuex from 'vuex'

Vue.use(Vuex)

//状态对象,用来存放具体数据
const state = {	//初始化组件中的数据
    counter: 0
}

//包含多个更新state函数的对象
const mutations = {
    
}

//包含多个对应事件回调函数的对象
const actions = {
    
}

//包含多个getter计算属性函数的对象
const getters = {
	evenOrOdd (state) {	//直接传入 state 即可访问数据
		return state.counter % 2 === 0 ? '偶数' : '奇数'
	}
}

export default new Vuex.Store({	//Store是构造函数,存在Vuex对象里面的对象
  state,	//状态对象,用来存放具体数据
  mutations,	//包含多个更新state函数的对象
  actions,		//包含多个对应事件回调函数的对象
  getters		//包含多个getter计算属性函数的对象
})	


​ 2)、使用定义好的计算属性。如下,使用上一步定义的计算属性 evenOrOdd

​ 在标签中用两个大括号将定义好的计算属性包含,这个方法和在 getters 中定义的名字可以不一样;

​ 在 computed 属性中定义上面大括号中的方法,然后使用 this.$store.getters.计算属性名 调用,注意,

​ 不要在后面加括号,因为计算属性获取时直接调用里面的 getter 和 setter 方法

<template>
	<div>
        <p>
            click {{$store.state.counter}} times, counter is {{evenOrOdd}}
        </p>
    </div>
</template>
<script>
	export default {
        
        computed: {
            evenOrOdd() {
                // 这里加括号是错误的,如 $store.getters.evenOrOdd() 是错误的
                // 不需要调用,只需要读取属性值
                return this.$store.getters.evenOrOdd
            }
        }
    }
</script>

​ 9、在组件中修改 state 管理的数据

​ 使用 $store 对象中有 dispatch(actionName, data) 方法去修改数据,点击增加或减少按钮修改数据

​ 1)、在组件中的代码如下:

​ 当点击增加或者减少的按钮修改数据时,会执行 methods 中相应的函数,函数中使用

​ this. s t o r e . d i s p a t c h ( ) 调 用 s t o r e 中 a c t i o n s 中 的 具 体 方 法 , t h i s . store.dispatch() 调用 store 中 actions 中的具体方法,this. store.dispatch()storeactionsthis.store.dispatch() 中传入的入参第一个

​ 是 store 中 actions 中定义的函数名称,第二个以后是参数

<template>
	<div>
        <!-- 点击下面按钮增加或减少 counter -->
        <button @click="increment">+</button>
        <button @click="decrement">-</button>
    </div>
</template>
<script>
	export default {
        
        methods: {
        	increment() {
                //通知 vuex 去增加
                this.$store.dispatch('increment')	//触发 store 中 action 调用
            },
            decrement() {
                this.$store.dispatch('decrement')
            }
    	}
    }
</script>

​ 2)、在 store.js 中的 actions 定义对应上面 dispatch 方法的第一个入参的方法,如 increment 方法,注

​ 意:方法中第一个参数是 {commit, state},commit 是一个方法,它提交给 mutations,最终导致

​ mutations 中的某一个方法调用,即执行 commit 第一个参数对应的方法

​ 执行异步操作都是在 actions 中执行的

/*
vuex最核心的管理对象store
 */
import Vue from 'vue'	//vuex是vue的插件,vue必须引入
import Vuex from 'vuex'

Vue.use(Vuex)

//状态对象,用来存放具体数据
const state = {	//初始化组件中的数据
    counter: 0
}

//包含多个更新state函数的对象
const mutations = {
    //增加的 mutation
    INCREMENT (state) {
        state.counter++
    },
    //减少的 mutation
    DECREMENT (state) {
        state.counter--
    }
}

//包含多个对应事件回调函数的对象
const actions = {
    increment({commit, state}) {
        commit('INCREMENT')
    },
    decrement({commit, state}) {
        commit('DECREMENT')
    }
}

//包含多个getter计算属性函数的对象
const getters = {
	evenOrOdd (state) {	//直接传入 state 即可访问数据
		return state.counter % 2 === 0 ? '偶数' : '奇数'
	}
}

export default new Vuex.Store({	//Store是构造函数,存在Vuex对象里面的对象
  state,	//状态对象,用来存放具体数据
  mutations,	//包含多个更新state函数的对象
  actions,		//包含多个对应事件回调函数的对象
  getters		//包含多个getter计算属性函数的对象
})	

​ 优化:

​ 上面的步骤中,有很多 this.$store,我们可以进行优化。

​ 在 script 标签中引入:import {mapState, mapGetters, mapActions} from ‘vuex’,引入后,在 computed

​ 计算属性中使用 …mapState([‘counter’]),…mapGetters([‘evenOrOdd’]),这样以后,直接可以在标签的

​ 大括号中使用 counter 或 evenOrOdd

​ 同样的,在 methods 中也可以使用 …mapActions,只需将方法名传入进去即可

​ 如果组件中定义的名称与 store 中定义的名字不一样,可以使用对象映射 {组件中的名字:store中的名字}

<template>
	<div>
        <p>
            click {{counter}} times,counter is {{evenOrOdd}}
        </p>
        <button @click='increment'>+</button>
        <button @click='decrement'>-</button>
    </div>
</template>

<script>
	import {mapState, mapGetters, mapActions} from 'vuex'
    export default {
        computed: {
            ...mapState(['counter']),
            ...mapGetters(['evenOrOdd'])
            //如果映射关系不一致,store.js 中getters中的方法名为 evenOrOdd2,我们可以用
            //...mapGetters({evenOrOdd: 'evenOrOdd2'}) 映射
            /*
            	以前的写法:
            		evenOrOdd() {
            			return this.$store.getters.evenOrOdd
            		},
            		counter() {
            			return this.$store.state.counter
            		}
            	其实,
            		...mapState(['counter']) 的返回值是一个对象,对象中使用的是一个函数,如:
            		{counter() {return this.$store.state['counter']}}
            		...mapGetters(['evenOrOdd']) 的返回值是一个对象,对象中使用的是一个函数,如:
            		{evenOrOdd() {return this.$store.state['evenOrOdd']}}
            	所以,其实是一样的,只是 vuex 给我们提供了一个简单的写法
            */
        },
        methods: {
            ...mapActions(['increment', 'decrement'])
        }
    }
</script>

Vuex结构图:

在这里插入图片描述

方式2:

​ 一般的,在项目中使用 vuex 时,会将 state、actions、mutations、getters 4个对象分别创建4个不同的文件

​ 步骤:

​ 1、在 src 下创建一个 store 文件夹,在文件夹中分别创建 state.js、getters.js、actions.js、mutations.js 以

​ 及入口 index.js 文件,如:

在这里插入图片描述

​ 2、index.js 文件中内容如下:

/*
vuex核心管理模块store对象
 */
import Vue from 'vue'
import Vuex from 'vuex'

import state from './state'
import mutations from './mutations'
import actions from './actions'
import getters from './getters'

Vue.use(Vuex)

export default new Vuex.Store({
  state,
  mutations,
  actions,
  getters
})

​ 3、在入口 main.js 文件中引入 store

/*
入口JS
 */
import Vue from 'vue'
import App from './App.vue'
import store from './store'

new Vue({
  el: '#app',
  components: {App}, // 映射组件标签
  template: '<App/>', // 指定需要渲染到页面的模板
  store // 所有的组件对象都多了一个属性: $store(值就是store对象)
})

​ 4、state.js、getters.js、actions.js、mutations.js 文件的写法如下:

​ 里面的具体内容还是和方式1一样,在组件中也可以写前面的优化方式

export default {
    
}

​ 需要注意的是,当有参数传入的时候,在 actions 文件中的入参还是跟普通传法一样,但是当调用 commit

​ 方法时,需要将入参包含在 {} 中,同样,在 mutations 中接收的时候也要使用{}包含,如:

​ actions.js中传参时调用 commit 时,需将参数用大括号括起来:

  addTodo ({commit}, todo) {
    // 提交一个comutation请求
    commit('add_todo', {todo}) // 传递给mutation的是一个包含数据的对象
  },

​ mutations.js 中同样要用大括号括起来:

  add_todo (state, {todo}) {  // 方法名不是ADD_TODO, 而是add_todo
    state.todos.unshift(todo)
  },

H、项目

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值