Vue 基础快速入门(一)

Vue

渐进式 JavaScript 框架,用来动态构建用户界面

特点:

  1. 遵循 MVVM 模式
  2. 编码简洁,体积小,运行效率高,适合 移动/PC 端开发
  3. 它本身只关注 UI,可以轻松引入 vue 插件或其它第三方库开发项目
  4. 采用组件化模式,提高代码复用率、且让代码更好维护
  5. 声明式编码,让编码人员无需直接操作 DOM,提高开发效率
  6. 使用虚拟 DOM 和 Diff 算法,尽量复用 DOM节点

借鉴 angular 的 模板数据绑定 技术
借鉴 react 的 组件化虚拟DOM 技术

引入 Vue

本地引入

CDN 引入

快速创建 Vue 的实例

概念性的问题:

  1. 想让 Vue 工作,就必须创建一个Vue实例,且要传入一个配置对象
  2. root 容器里的代码被称为 Vue 模板
  3. Vue 实例和容器是一一对应的
  4. 真实开发中只有一个 Vue 实例,并且会配合着组件一起使用

el: 又称挂载点,可认为是 element 的简写,创建一个 vue实例 得知道是在哪一块元素上创建 Vue实例 ,对哪一块视图进行操作

var vm = new Vue({
    el: '#root',
    data: {
        msg: '123'
    },
    methods: {
        test() {     
        }
    }
})

el 和 data 的两种写法

data 与 el 的2种写法

  1. el 有2种写法
    (1) new Vue 时候配置 el 属性
    (2) 先创建 Vue 实例,随后再通过 vm.$mount(’#root’) 指定 el 的值
  2. data 有2种写法
    (1) 对象式
    (2) 函数式
    如何选择: 目前哪种写法都可以,以后学习到组件时,data 必须使用函数式,否则会报错
  3. 一个重要的原则:
    由 Vue 管理的函数,一定不要写箭头函数,一旦写了箭头函数,this就不再是 Vue 实例了
    Vue 管理的函数举例:data(), method方法中定义的函数等

Vue 的插值语法

{{js 表达式}} 用于解析标签体内容

<div id="test">
    <input type="text" v-model="msg"><br>
     <!--调用函数-->
    <p>Hello {{msg.toUpperCase()}} </p>
</div>

<script src="CDN 引入或者本地引入vue.js"></script>
<script>
    const vm = new Vue({ // 配置对象 options 
        // 配置选项(option)
        el: '#test', // element: 指定用vue来管理页面中的哪个标签区域
        data: { // 数据(model)
            msg: 'World'
        }
    })
</script>

理解 MVVM

M: 模型(Model) :data中的数据
V: 视图(View) :模板代码 (不是静态页面) (两个语法:指令,大括号表达式)
VM: viewModel: 视图模型 (Vue的实例)

  • Dom Listeners (Dom 监听)
  • Data Bindings (数据绑定)

MVVM 本质上是 MVC (Model-View- Controller)的改进版。即模型-视图-视图模型。

模型 model 指的是后端传递的数据,视图 view 指的是所看到的页面。

视图模型 viewModel 是 mvvm 模式的核心,它是连接 view 和 model 的桥梁。它有两个方向:

模型转化成视图,即将后端传递的数据转化成所看到的页面。实现的方式是:数据绑定
视图转化成模型,即将所看到的页面转化成后端的数据。实现的方式是:DOM 事件监听

这两个方向都实现的,我们称之为数据的双向绑定


  1. data 中所有的属性,最后都出现在了 vm 身上。
  2. vm 身上所有的属性 及 Vue 原型上所有属性,在 Vue 模板中都可以直接使用

理解模板

  • 动态 html 页面
  • 包含了 js 代码

插值语法: {{xxx}} 用于解析标签体的内容, 可以向页面输出数据
指令语法: v- 开头的自定义标签属性

数据绑定

v-bind: 数据绑定 (指定变化的属性值)

v-bind:xxx='yy' // yy 会被作为表达式解析
// 简写
:xxx='yy'

特点: 数据只能从 data 流向页面, 是单向数据绑定

v-model: 双向的数据绑定

数据能从 data 流向页面, 还能从页面流向 data

只能应用于表单类元素 (输入类元素) 上

收集表单数据:
若:<input type="text"/>,则v-model默认收集的是value值,用户输入的就是value值。
若:<input type="password"/>,则v-model默认收集的是value值,用户输入的就是value值。
若:<input type="radio"/>,则v-model默认收集的是value值,因为此类型无法输入内容,则无法通过输入得到value值,所以要给标签手动添加value值。
若:<input type="checkbox"/>
    1.没有配置input的value属性,那么默认读取的的就是checked是否被勾选(勾选 or 未勾选,是布尔值)
    2.配置input的value属性:
	(1)v-model的初始值是非数组,那么收集的就是checked(勾选 or 未勾选,是布尔值)
	(2)v-model的初始值是数组,那么收集的的就是value组成的数组

v-model 的三个修饰符
lazy:失去焦点再收集数据
number:输入字符串转为有效的数字
trim:输入首尾空格过滤

v-model:value='yy'
// 简写  因为 v-model 默认收集的就是 value 值
v-model='yy'

v-text: 向所在节点渲染文本内容

和插值语法的区别: v-text 会替换节点的内容, 插值语法不会

v-html: 像节点中渲染包含 html 结构的内容

和插值语法的区别:

  1. v-html 会替换节点所有内容, 插值语法不会
  2. v-html 可以识别 html 结构
  3. 注意: v-html 有安全性问题, 在网站动态渲染任意 html 是非常危险的, 容易遭受 XSS 攻击, 要在可信的内容使用 v-html, 不要使用在用户提交的内容上
<body>
    <div id='app'>

        <h2>1. 大括号表达式</h2>
        <p>{{msg}}</p>    <!--textContent -->
        <p>{{msg.toUpperCase()}}</p>

        <p v-html="msg"></p> <!--innerHTML -->

        <p v-text="msg"></p> <!--textContent -->
        <p v-text="msg.toUpperCase()"></p>

        <h2>2. 指令一: 强制数据绑定</h2>
        <img src="imgUrl" alt="Vue">  <!--无法显示图片,没有识别成js表达式 -->
        <img v-bind:src="imgUrl" alt="Vue"> <!--属性值识别成js表达式 -->
        <img :src="imgUrl" alt="Vue">

        <h2>3. 指令二: 绑定事件监听</h2>
        <button v-on:click="test1">test1</button>
        <button @click="test1">test1</button>

        <button @click="test2('abc')">test2</button> <!--可以传参数 -->
        <button @click="test2(msg)">test2</button>
    </div>

    <script src="../js/vue.js"></script>
    <script>
        new Vue({
            el: '#app',
            data: {
                msg: '<a href="http:www.baidu.com">I Will Back!</a>',
                imgUrl: "https://cn.vuejs.org/images/logo.png"
            },
            methods: {
                test1() {
                    alert('heheh');
                },
                test2(content){
                    alert(content);
                }
            }

        })
    </script>
</body>

Object.defineProperty()

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8" />
        <title>回顾Object.defineproperty方法</title>
    </head>
    <body>
        <script type="text/javascript">
            let number = 18
            let person = {
                name: '张三',
                sex: '男',
            }
            //Object.defineProperty给对象添加属性
            Object.defineProperty(person, 'age', {
                //里面三个参数(对象,属性名,options配置对象{})
                // value: 18,
                // enumerable: true, //控制属性是否可以枚举(遍历),默认值是false
                // writable:true, //控制属性是否可以被修改,默认值是false
                // configurable:true //控制属性是否可以被删除,默认值是false

                //当有人读取person的age属性时,get函数(getter)就会被调用,且返回值就是age的值
                get() {
                    console.log('有人读取age属性了')
                    return number
                },

                //当有人修改person的age属性时,set函数(setter)就会被调用,且会收到修改的具体值
                set(value) {
                    console.log('有人修改了age属性,且值是', value)
                    number = value
                }

            })
            // console.log(Object.keys(person))
            console.log(person)
        </script>
    </body>
</html>

事件

v-on: 绑定事件监听

绑定指定事件名的回调函数

v-on:click='xxx'
v-on:keyup='xxx(参数)'
v-on:keyup.enter='xxx'
// 简写
@click='xxx'
@keyup='xxx'
@keyup.enter='xxx'

事件修饰符

  1. prevent:阻止默认事件
  2. stop:阻止事件冒泡
  3. once:事件只触发一次
  4. capture:使用事件的捕获模式
  5. self:只有event.target是当前操作的元素时才触发事件
  6. passive:事件的默认行为立即执行,无需等待事件回调执行完毕

键盘事件

Vue 中常用的按键别名
回车 => enter
删除 => delete (捕获“删除”和“退格”键)
退出 => esc
空格 => space
换行 => tab (特殊,必须配合keydown去使用)
上 => up
下 => down
左 => left
右 => right

系统修饰键(用法特殊):tab、ctrl、alt、shift、meta(win)
(1) 配合 keyup 使用: 按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发
(2) 配合 keydown 使用: 正常触发事件

<div id="root"> 
    <h2>欢迎来到{{name}}学习</h2>
    <input type="text" placeholder="输入" @keydown.huiche="showInfo">
    <!-- <input type="text" placeholder="提示输入" @keydown.enter="showInfo"> -->
    <!--当有需求要同时按下两个键才能生效时 
    <input type="text" placeholder="提示输入" @keyup.ctrl.y="showInfo"> -->
</div>

计算属性

只要 data 中的数据发生改变, Vue 会重新解析模板

  • computed 属性对象中定义计算属性的方法
  • 在页面中使用{{方法名}}来显示计算的结果

原理:底层借助了 Objcet.defineproperty 方法提供的 gettersetter

get 函数什么时候执行?
(1) 初次读取时会执行一次。
(2) 当依赖的数据发生改变时会被再次调用。
优势:与 methods 实现相比,内部有缓存机制, 效率更高,调试方便。
备注:

  1. 计算属性最终会出现在vm上,直接读取使用即可。
  2. 如果计算属性要被修改,那必须写 set 函数去响应修改,且 set 中要引起计算时依赖的数据发生改变
//完整写法
 fullName:{
	get(){
		console.log('get被调用了')
		return this.firstName + '-' + this.lastName
	},
	set(value){
		console.log('set',value)
		const arr = value.split('-')
		this.firstName = arr[0]
		this.lastName = arr[1]
	}
} 

简写 (只读不改就可以用简写)

//简写
fullName(){
	console.log('get被调用了')
	return this.firstName + '-' + this.lastName
}

监视属性

通过 vm 的 $watch()watch 配置来监视指定的属性 (可以监视计算属性中的属性)

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

  • 监视的属性必须存在,才能进行监视

两种写法:

  • new Vue 时传入 watch 配置

    watch: {
    	isHot: {
    		immediate: true, //初始化时让 handler 调用一下
    		//当 isHot 发生改变时, 调用 handler
    		handler(newValue, oldValue){
    			console.log('isHot被修改了',newValue,oldValue)
    		}
    	}
    }
    
  • 通过 vm.$watch 监视

    vm.$watch('isHot',{
    	immediate: true, //初始化时让 handler 调用一下
    	handler(newValue,oldValue){
    		console.log('isHot被修改了',newValue,oldValue)
    	}
    })
    

深度监视

监视某个 number 的 a 属性

'numbers.a':{
	deep:true,
	handler(){
		console.log('numbers改变了')
	}
}

(1) Vue 中的 watch 默认不监测对象内部值的改变 (一层)
(2) 配置 deep:true 可以监测对象内部值改变 (多层)
备注:
(1) Vue 自身可以监测对象内部值的改变,但 Vue 提供的 watch 默认不可以!
(2) 使用 watch 时根据数据的具体结构,决定是否采用深度监视

numbers:{
	deep:true,
	handler(){
		console.log('numbers改变了')
	}
}

一般写法

isHot:{
	// immediate:true, //初始化时让 handler 调用一下
	// deep:true,//深度监视
	handler(newValue,oldValue){
		console.log('isHot被修改了',newValue,oldValue)
	}
}

简写

//简写
isHot(newValue,oldValue){
	console.log('isHot被修改了',newValue,oldValue,this)
} 

计算属性和监视属性的区别

  1. computed 能完成的功能,watch 都可以完成
  2. watch 能完成的功能,computed 不一定能完成,例如: watch 可以进行异步操作

重要原则

  1. 所被 Vue 管理的函数,最好写成普通函数,这样 this 的指向才是 vm 或 组件实例对象
  2. 所有不被 Vue 所管理的函数 (定时器的回调函数、ajax 的回调函数等、Promise 的回调函数), 最好写成箭头函数,这样 this 的指向才是 vm 或组件实例对象

样例代码

<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 Name1" v-model="fullName1"><br>
        姓名2(单向):<input type="text" placeholder="Full Name2" v-model="fullName2"><br>
        姓名3(双向):<input type="text" placeholder="Full Name3" v-model="fullName3"><br>

        <p>{{fullName1}}</p>
        <p>{{fullName1}}</p>
        <p>{{fullName1}}</p>

    </div>

    <script src="../js/vue.js"></script>
    <script>
        const vm = new Vue({
            el: '#demo',
            data: {
                firstName: 'A',
                lastName: 'B',
                fullName2: 'A B'
            },
            computed: {
                // 什么时候执行:初始化显示 / 相关的data属性数据发生改变
                // 做什么的: 计算并返回当前属性的值
                fullName1() { //计算属性中的一个方法,方法的返回值作为属性 (回调函数)
                    console.log('fullName1()') // 必然会掉用
                    return this.firstName + ' ' + this.lastName;
                },

                fullName3: { //不是函数是一个对象,里面有两个方法  
                    get() {
                        // 回调函数 : 1. 你定义的 2. 你没有调用 3. 但他最终执行了
                        // 1. 什么时候调用? 2. 用来做什么
                        // 回调函数 当需要读取当前属性值时回调  根据相关的数据计算并返回当前属性的值
                        return this.firstName + ' ' + this.lastName;
                    },

                    set(value) {
                        // 回调函数 监视当前属性值的变化 当属性值发生改变时回调  更新相关的属性数据
                        const names = value.split(' ');
                        this.firstName = names[0];
                        this.lastName = names[1];
                    }
                }
            },

            watch: { //配置监视
                firstName: function(value){ // firstName 发生了变化
                    console.log(this); //就是vm对象
                    this.fullName2 = value + ' ' + this.lastName;
                }
            }
        });

        vm.$watch('lastName', function(value) {
            //更新fullName2
            this.fullName2 = this.firstName + ' ' + value;
        })
    </script>

绑定样式

1. class 样式

写法 :class="xxx" xxx可以是字符串、对象、数组。
字符串写法适用于:类名不确定,要动态获取。
数组写法适用于:要绑定多个样式,个数不确定,名字也不确定。
对象写法适用于:要绑定多个样式,个数确定,名字也确定,但不确定用不用。

2. style 样式

	:style="{fontSize: xxx}"其中xxx是动态值。
	:style="[a,b]"其中a、b是样式对象。
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8" />
        <title>绑定样式</title>
        <style>
            .basic {
                width: 400px;
                height: 100px;
                border: 1px solid black;
            }
            .happy {
                border: 4px solid red;
                ;
                background-color: rgba(255, 255, 0, 0.644);
                background: linear-gradient(30deg, yellow, pink, orange, yellow);
            }
            .sad {
                border: 4px dashed rgb(2, 197, 2);
                background-color: gray;
            }
            .normal {
                background-color: skyblue;
            }
            .atguigu1 {
                background-color: yellowgreen;
            }
            .atguigu2 {
                font-size: 30px;
                text-shadow: 2px 2px 10px red;
            }
            .atguigu3 {
                border-radius: 20px;
            }
        </style>
        <script type="text/javascript" src="../js/vue.js"></script>
    </head>
    <body>
        <!-- 准备好一个容器-->
        <div id="root">
            <!-- 绑定class样式--字符串写法,适用于:样式的类名不确定,需要动态指定 -->
            <div class="basic" :class="mood" @click="changeMood">{{name}}</div> <br /><br />

            <!-- 绑定class样式--数组写法,适用于:要绑定的样式个数不确定、名字也不确定 -->
            <div class="basic" :class="classArr">{{name}}</div> <br /><br />

            <!-- 绑定class样式--对象写法,适用于:要绑定的样式个数确定、名字也确定,但要动态决定用不用 -->
            <div class="basic" :class="classObj">{{name}}</div> <br /><br />

            <!-- 绑定style样式--对象写法 -->
            <div class="basic" :style="styleObj">{{name}}</div> <br /><br />
            <!-- 绑定style样式--数组写法 -->
            <div class="basic" :style="styleArr">{{name}}</div>
        </div>
    </body>
    <script type="text/javascript">
        Vue.config.productionTip = false
        const vm = new Vue({
            el: '#root',
            data: {
                name: '我要进大厂',
                mood: 'normal',
                classArr: ['atguigu1', 'atguigu2', 'atguigu3'],
                classObj: {
                    atguigu1: true,
                    atguigu2: false,
                },
                styleObj: {
                    fontSize: '40px',
                    color: 'red',
                },
                styleObj2: {
                    backgroundColor: 'orange'
                },
                styleArr: [
                    {
                        fontSize: '40px',
                        color: 'blue',
                    },
                    {
                        backgroundColor: 'gray'
                    }
                ]
            },
            methods: {
                changeMood() {
                    //随机切换心情
                    const arr = ['happy', 'sad', 'normal']
                    const index = Math.floor(Math.random() * 3)
                    //Math.random()	返回 0 ~ 1 之间的随机数,包含 0 不包含 1。
                    //Math.floor(x)	对 x 进行下舍入,即向下取整。
                    this.mood = arr[index]
                }
            },
        })
    </script>
</html>

条件渲染

v-if
v-else-if
v-else
适用于: 切换频率较低的场景
特点: 不展示的 DOM 元素直接被移除

v-show

适用于: 切换频率较高的场景。
特点: 不展示的 DOM 元素未被移除,仅仅是使用样式隐藏掉

注: v-if 的时候,元素可能无法获取到,而使用 v-show 一定可以获取到

比较 v-if 和 v-show

  • 如果需要频繁切换 v-show 较好
  • 当条件不成立时, v-if 的所有子节点不会解析
// v-if 和 template 配合使用
<template v-if="n === 1">
	<h2>hi</h2>
    <h2>hi</h2>
</template>

列表渲染

v-for

  1. 用于展示列表数据
  2. 可遍历:数组、对象、字符串(用的很少)、指定次数(用的很少)
  3. v-for 指令需要使用 item in items 形式的特殊语法,其中 items 是源数据数组,而 item 则是被迭代的数组元素的别名(形参)
  4. v-for 还支持一个可选的第二个参数,即当前项的索引。
  5. 语法:v-for="(item, index) in xxx" :key=“yyy”
  6. 你也可以用 of 替代 in 作为分隔符,因为它更接近 JavaScript 迭代器的语法

key 的原理

有相同父元素的子元素必须有独特的 key, 重复的 key 会造成渲染错误

react、vue中的key有什么作用? (key的内部原理)

  1. 虚拟DOM中key的作用:
    key是虚拟DOM对象的标识,当数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】,
    随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较,比较规则如下:

  2. 对比规则:
    (1) 旧虚拟 DOM 中找到了与新虚拟 DOM 相同的 key:
    ①.若虚拟 DOM 中内容没变, 直接使用之前的真实 DOM!
    ②.若虚拟 DOM 中内容变了, 则生成新的真实 DOM,随后替换掉页面中之前的真实 DOM。
    (2) 旧虚拟 DOM 中未找到与新虚拟DOM相同的key创建新的真实 DOM,随后渲染到到页面。

  3. 用 index 作为 key 可能会引发的问题:
    1.若对数据进行:逆序添加、逆序删除等破坏顺序操作:
    会产生没有必要的真实 DOM 更新 ==> 界面效果没问题, 但效率低。

    2.如果结构中还包含输入类的 DOM: 会产生错误 DOM 更新 ==> 界面有问题。

  4. 开发中如何选择 key?
    1.最好使用每条数据的唯一标识作为 key, 比如 id、手机号、身份证号、学号等唯一值。
    2.如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用 index 作为 key 是没有问题的。
    3.如果不写 key,则默认为 index

列表过滤

用 computed 实现 (推荐)

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8" />
        <title>列表过滤</title>
        <script type="text/javascript" src="../js/vue.js"></script>
    </head>
    <body>
        <!-- 1.收集用户输入
2.拿用户输入的东西进行数据匹配
-->
        <!-- 准备好一个容器-->
        <div id="root">
            <h2>人员列表</h2>
            <input type="text" placeholder="请输入名字" v-model="keyWord">
            <ul>
                <li v-for="(p,index) of filPerons" :key="index">
                    {{p.name}}-{{p.age}}-{{p.sex}}
                </li>
            </ul>
        </div>
        <script type="text/javascript">
            Vue.config.productionTip = false
            // 用 computed 实现
            new Vue({
                el: '#root',
                data: {
                    keyWord: '',
                    persons: [
                        { id: '001', name: '马冬梅', age: 19, sex: '女' },
                        { id: '002', name: '周冬雨', age: 20, sex: '女' },
                        { id: '003', name: '周杰伦', age: 21, sex: '男' },
                        { id: '004', name: '温兆伦', age: 22, sex: '男' }
                    ]
                },
                computed: {
                    filPerons() {
                        return this.persons.filter((p) => {
                            return p.name.indexOf(this.keyWord) !== -1
                        })
                    }
                }
            })
        </script>
        </html>

用 watch 实现

//将下面代码放入上方代码块中
new Vue({
    el: '#root',
    data: {
        keyWord: '',
        persons: [
            { id: '001', name: '马冬梅', age: 19, sex: '女' },
            { id: '002', name: '周冬雨', age: 20, sex: '女' },
            { id: '003', name: '周杰伦', age: 21, sex: '男' },
            { id: '004', name: '温兆伦', age: 22, sex: '男' }
        ],
        filPerons: []
    },
    watch: {
        keyWord: {
            immediate: true,
            // 初始化的时候让handler调用一下,因为开始filPerons: []为空数组,
            // 页面上什么内容都不显示,而恰巧handler(val)获取的值为空,
            // 而所有字符串第0位不仅包含自己第一个字符,也包含着一个空字符,p.name.indexOf('')为0 
            // 所以handler(val)函数判断成功,页面显示内容
            handler(val) {
                this.filPerons = this.persons.filter((p) => {
                    return p.name.indexOf(val) !== -1
                    // 判断p.name中是否包含有val值,如果不等于-1,则说明包含
                    // filter返回一个全新的数组,原数组不变
                })
            }
        }
    }
})

列表排序

列表排序与列表过滤一般是连在一起使用的

<!DOCTYPE html>
<html>

    <head>
        <meta charset="UTF-8" />
        <title>列表排序</title>
        <script type="text/javascript" src="../js/vue.js"></script>
    </head>

    <body>
        <!-- 准备好一个容器-->
        <div id="root">
            <h2>人员列表</h2>
            <input type="text" placeholder="请输入名字" v-model="keyWord">
            <button @click="sortType = 2">年龄升序</button>
            <button @click="sortType = 1">年龄降序</button>
            <button @click="sortType = 0">原顺序</button>
            <ul>
                <li v-for="(p,index) of filPerons" :key="p.id">
                    {{p.name}}-{{p.age}}-{{p.sex}}
                    <input type="text">
                </li>
            </ul>
        </div>

        <script type="text/javascript">
            Vue.config.productionTip = false
            new Vue({
                el: '#root',
                data: {
                    keyWord: '',
                    sortType: 0, //0原顺序 1降序 2升序
                    persons: [
                        { id: '001', name: '马冬梅', age: 30, sex: '女' },
                        { id: '002', name: '周冬雨', age: 31, sex: '女' },
                        { id: '003', name: '周杰伦', age: 18, sex: '男' },
                        { id: '004', name: '温兆伦', age: 19, sex: '男' }
                    ]
                },
                computed: {
                    filPerons() {
                        const arr = this.persons.filter((p) => {
                            return p.name.indexOf(this.keyWord) !== -1
                        })
                        //判断一下是否需要排序
                        if (this.sortType !== 0) {
                            arr.sort((p1, p2) => {
                                return this.sortType === 1 ? p2.age - p1.age : p1.age - p2.age
                            })
                        }
                        return arr
                    }
                }
            })
        </script>
        </html>

监视数据原理

更新时的问题

为什么 this.person[0] = {id:'001', name:'马老师', age:50, sex:'男'} Vue 没有检测到改变 ?

Vue监视数据的原理:

  1. vue 会监视 data 中所有层次的数据。
  2. 如何监测对象中的数据?
    通过 setter 实现监视,且要在 new Vue 时就传入要监测的数据。
    (1) 对象中后追加的属性,Vue 默认不做响应式处理,即改变其值页面无变化
    (2) 如需给后添加的属性做响应式,请使用如下API:
    Vue.set(target,propertyName/index,value) 或
    vm.$set(target,propertyName/index,value)
    // vm.$set(添加目标,‘添加属性/索引值’,‘属性值’)
  3. 如何监测数组中的数据?
    通过包裹数组更新元素的方法实现,本质就是做了两件事:
    (1) 调用原生对应的方法对数组进行更新
    (2) 重新解析模板,进而更新页面
  4. 在 Vue 修改数组中的某个元素一定要用如下方法:
    1.使用这些API: push()、pop()、shift() (删除第一个)、unshift()、splice() (删除, 替换, 插入 指定位置元素)、sort()、reverse()
    2.Vue.set() 或 vm.$set()
    3.如果不使用上面方法而是直接对数组赋值则 vue 无法响应,例如:
    this.student.hobby[0] = “开车”,数据已被更改,但页面中无任何反应
    特别注意:Vue.set() 和 vm.$set() 不能给vm 或 vm的根数据对象(vm._data) 添加属性
    对于 filter 如何让 Vue 监测? 直接把过滤后的数组直接传给原数组
array.splice(index, howmany, item1, ....., itemX)
// index 必须, 整数,指定在什么位置添加/删除项目,使用负值指定从数组末尾开始的位置
// howmany 可选, 要删除的项目数, 如果设置为 0,则不会删除任何项目
// 可选, 要添加到数组中的新项目

过滤器

对要显示的数据进行特定格式化后再显示(适用于一些简单逻辑的处理)
语法:

  1. 注册过滤器:Vue.filter(name,callback) 或 new Vue{filters:{}}
  2. 使用过滤器:{{ xxx | 过滤器名 }} 或 v-bind:属性 = “xxx | 过滤器名”

备注:

  1. 过滤器也可以接收额外参数、多个过滤器也可以串联
  2. 并没有改变原本的数据, 是产生新的对应的数据
  3. 不是必须的属性,完全可以用 methods 和 computed 实现下面代码中的过滤功能
  4. 当全局过滤器和局部过滤器重名时,会采用局部过滤器
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8" />
        <title>过滤器</title>
        <script type="text/javascript" src="../js/vue.js"></script>
        <!-- <script type="text/javascript" src="../js/dayjs.min.js"></script> -->
        <script src="https://cdn.bootcdn.net/ajax/libs/dayjs/1.10.6/dayjs.min.js"></script>
    </head>
    <body>
        <!-- 准备好一个容器-->
        <div id="root">
            <h2>显示格式化后的时间</h2>
            <!-- 计算属性实现 -->
            <h3>计算属性实现:{{fmtTime}}</h3>
            <!-- methods实现 -->
            <h3>methods实现:{{getFmtTime()}}</h3>
            <!-- 过滤器实现 -->
            <h3>过滤器实现:{{time | timeFormater}}</h3> //将time当参数传给timeFormater
            <!-- 过滤器实现(传参) -->
            <h3>过滤器实现(传参):{{time | timeFormater('YYYY_MM_DD') | mySlice}}</h3>
            <h3 :x="msg | mySlice">{{msg}}</h3>
        </div>
        <div id="root2">
            <h2>{{msg | mySlice}}</h2>
        </div>
    </body>
    <script type="text/javascript">
        Vue.config.productionTip = false
        //全局过滤器,必须在new Vue({})之前
        Vue.filter('mySlice', function (value) {
            return value.slice(0, 4)
        })
        new Vue({
            el: '#root',
            data: {
                time: 1621561377603, //时间戳
                msg: '你好,尚硅谷'
            },
            computed: {
                fmtTime() {
                    return dayjs(this.time).format('YYYY年MM月DD日 HH:mm:ss')
                }
            },
            methods: {
                getFmtTime() {
                    return dayjs(this.time).format('YYYY年MM月DD日 HH:mm:ss')
                }
            },
            // filters: {
            // 	timeFormater(value) {
            // 		return dayjs(value).format('YYYY年MM月DD日 HH:mm:ss')
            // 	}
            // }
            //局部过滤器
            filters: {
                //如果不传参数,则使用默认参数'YYYY年MM月DD日 HH:mm:ss',如果传参数,则使用
                //传入的参数'YYYY_MM_DD'
                timeFormater(value, str = 'YYYY年MM月DD日 HH:mm:ss') {
                    // console.log('@',value)
                    return dayjs(value).format(str)
                }
            }
        })
        new Vue({
            el: '#root2',
            data: {
                msg: 'hello,atguigu!'
            }
        })
    </script>
</html>

常用指令

v-cloak 指令

  1. 本质是一个特殊属性,Vue 实例创建完毕并接管容器后,会删掉 v-cloak 属性
  2. 使用 css 配合 v-cloak 可以解决网速慢时页面展示出模板 {{xxx}} 的问题
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8" />
        <title>v-cloak指令</title>
        <style>
            [v-cloak] {
                display: none;
            }
        </style>
        <!-- 引入Vue -->
    </head>
    <body>
        <div id="root">
            <h2 v-cloak>{{name}}</h2>
        </div>
        <script type="text/javascript" src="http://localhost:8080/resource/5s/vue.js"></script>
    </body>

    <script type="text/javascript">
        new Vue({
            el: '#root',
            data: {
                name: '尚硅谷'
            }
        })
    </script>
</html>

v-once

  1. v-once 所在节点在初次动态渲染后,就视为静态内容了
  2. 以后数据的改变不会引起 v-once 所在结构的更新,可以用于优化性能
<div id="root">
    <h2 v-once>初始化的n值是:{{n}}</h2>
    <h2>当前的n值是:{{n}}</h2>
    <button @click="n++">点我n+1</button>
</div>

v-pre

用于vue性能优化

  1. 跳过其所在节点的编译过程
  2. 可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译
<div id="root">
    <h2 v-pre>Vue其实很简单</h2>
    <h2 >当前的n值是:{{n}}</h2>
    <button @click="n++">点我n+1</button>
</div>

自定义指令

需求1:定义一个 v-big 指令,和 v-text 功能类似,但会把绑定的数值放大 10 倍
需求2:定义一个 v-fbind 指令,和 v-bind 功能类似,但可以让其所绑定的 input 元素默认获取焦点

语法:

// 局部指令
new Vue({
    directives: {
        指令名: 配置对象
    }
})
new Vue({
    directives: {
        指令名: 回调函数
    }
})
// 全局指令
Vue.directive(指令名, 配置对象)
Vue.directive(指令名, 回调函数)

二、配置对象中常用的3个回调:
(1) bind: 指令与元素成功绑定时调用。
(2) inserted: 指令所在元素被插入页面时调用。
(3) update: 指令所在模板结构被重新解析时调用。

三、备注:

1. 指令定义时不加 v-,但使用时要加 v-
2. 指令名如果是多个单词,要使用 kebab-case 命名方式,别忘了加"", 不要用 camelCase 命名
3. 所有指令相关的 this 都是 window
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8" />
        <title>自定义指令</title>
        <script type="text/javascript" src="../js/vue.js"></script>
    </head>
    <body>
        <!-- 准备好一个容器-->
        <div id="root">
            <h2>{{name}}</h2>
            <h2>当前的n值是:<span v-text="n"></span> </h2>
            <!-- <h2>放大10倍后的n值是:<span v-big-number="n"></span> </h2> -->
            <h2>放大10倍后的n值是:<span v-big="n"></span> </h2>
            <button @click="n++">点我n+1</button>
            <hr />
            <input type="text" v-fbind:value="n">
        </div>
    </body>

    <script type="text/javascript">
        Vue.config.productionTip = false
        //定义全局指令
        /* Vue.directive('fbind',{
		//指令与元素成功绑定时(一上来)
		bind(element,binding){
			element.value = binding.value
		},
		//指令所在元素被插入页面时
		inserted(element,binding){
			element.focus()
		},
		//指令所在的模板被重新解析时
		update(element,binding){
			element.value = binding.value
		}
	})
	Vue.directive('big',function(element, binding) { //两个参数:当前DOM元素(span),本次绑定的所有信息
				console.log(element, binding)
				// console.log('big', this) 
				//注意此处的this是window
				element.innerText = binding.value * 10
			},)
 */
        new Vue({
            el: '#root',
            data: {
                name: '尚硅谷',
                n: 1
            },
            directives: {
                //指令名如果是多个单词,别忘了加"",方法内部放的是"key",value值,大部分情况""可省略,但加了-之后引号必须带。
                // 全写是: 'big-number':function(element,binding){} 
                /* 'big-number'(element,binding){
				// console.log('big')
				element.innerText = binding.value * 10
			}, */

                // big函数何时会被调用?1.指令与元素成功绑定时(一上来)。2.指令所在的模板被重新解析时。
                big(element, binding) { //两个参数:当前DOM元素(span),本次绑定的所有信息
                    console.log(element, binding)
                    // console.log('big', this) 
                    //注意此处的this是window
                    element.innerText = binding.value * 10
                },
                fbind: {
                    //指令与元素成功绑定时(一上来)调用
                    bind(element, binding) {//两个参数:当前DOM元素(input),本次绑定的所有信息
                        element.value = binding.value
                    },
                    //指令所在元素被插入页面时调用
                    inserted(element, binding) {
                        element.focus()
                        // input输入框自动获取焦点,这句代码必须放在将input放入页面的后面
                    },
                    //指令所在的模板被重新解析时调用
                    update(element, binding) {
                        element.value = binding.value
                        element.focus()
                    }
                }
            }
        })
    </script>
</html>

生命周期

  • 又叫做生命周期回调函数、生命周期函数、生命周期钩子
  • 是 Vue 在关键时刻帮我们调用的一些特殊名称的函数
  • 生命周期函数的名字不可更改,但函数的具体内容是程序员根据需求编写的
  • 生命周期函数中的 this 指向是 vm 或 组件实例对象, 这意味着你不能使用箭头函数来定义一个生命周期方法 (例如 created: () => this.fetchTodos())
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8" />
        <title>引出生命周期</title>
        <!-- 引入Vue -->
        <script type="text/javascript" src="../js/vue.js"></script>
    </head>
    <body>				
        <div id="root">
            <h2 :style="{opacity:opacity}">欢迎学习Vue</h2>
            <button @click="opacity = 1">透明度设置为1</button>
            <button @click="stop">点我停止变换</button>
        </div>
    </body>

    <script type="text/javascript">
        const vm = new Vue({
            el: '#root',
            data: {
                opacity: 1
            },
            methods: {
                stop() {
                    this.$destroy()
                }
            },
            // Vue完成模板的解析并把初始的真实DOM元素放入页面后(挂载完毕)调用mounted(挂载),只是初始化的时候调用了一次,后续数据更改重新解析模板时不会再次调用
            mounted() {
                console.log('mounted', this)
                this.timer = setInterval(() => {
                    // 这里箭头函数无this,自动寻找上下文this,找到了mounted()的this,这个this指向vm
                    console.log('setInterval')
                    this.opacity -= 0.01
                    if (this.opacity <= 0) this.opacity = 1
                }, 16)
            },
            beforeDestroy() {
                console.log('vm即将被销毁')
                clearInterval(this.timer)
                // 清除定时器
            },
        })

        //通过外部的定时器实现(不推荐)
        // setInterval(() => {
        // 	vm.opacity -= 0.01
        // 	if (vm.opacity <= 0) { vm.opacity = 1 }
        // }, 16)
    </script>
</html>

生命周期流程

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8" />
        <title>分析生命周期</title>
        <!-- 引入Vue -->
        <script type="text/javascript" src="../js/vue.js"></script>
    </head>

    <body>
        <!-- 准备好一个容器-->
        <div id="root" :x="n">
            <h2 v-text="n"></h2>
            <h2>当前的n值是:{{n}}</h2>
            <button @click="add">点我n+1</button>
            <button @click="bye">点我销毁vm</button>
        </div>
    </body>
    <script type="text/javascript">
        const vm = new Vue({
            el: '#root',
            // template: `
            // 	<div>
            // 		<h2>当前的n值是:{{n}}</h2>
            // 		<button @click="add">点我n+1</button>
            // 	</div>
            // `,
            //使用template模板,容器内就不用放入内容了,不过template模板解析的时候会将外面的root容器给覆盖掉。
            // 而且template模板只能有一个根元素,所以必须用div 将h2与button包裹起来,否则报错
            data: {
                n: 1
            },
            methods: {
                add() {
                    console.log('add')
                    this.n++
                },
                bye() {
                    console.log('bye')
                    this.$destroy()
                    // 调用销毁函数,但之前的工作成果还在,只是以后不能管理了
                }
            },
            watch: {
                n() {
                    console.log('n变了')
                }
            },
            beforeCreate() { //看图:这里是指数据代理和数据监测创建之前,不是vm
                console.log('beforeCreate')
                // console.log(this);
                // debugger
                // 此时打开控制台可以看到data中无数据,无methods方法
            },
            created() {
                console.log('created')
                // console.log(this);
                // debugger
                // 此时打开控制台可以看到data中有数据,有add,bye方法
            },
            beforeMount() {
                console.log('beforeMount')
                // console.log(this);
                // debugger
                // 元素都加载完成(未经编译),但还没有挂载上去,HTML的body结构里呈现的依然是模板
                // 在这里面操作DOM白操作,最终会被虚拟dom转换成的真实dom覆盖掉
            },
            mounted() {
                console.log('mounted')
                // console.log(this);
                // debugger
                // 元素都加载完成(编译完成),已经挂载上去了,HTML的body结构里呈现的你想让他呈现的样子
                // 在这里面操作DOM有效,但不推荐
            },
            beforeUpdate() {
                console.log('beforeUpdate')
                // console.log(this.n);
                // debugger
                // 更新数据时调用,数据为新的,但页面还是旧的,尚未更新
            },
            updated() {
                console.log('updated')
                // console.log(this.n);
                // debugger
                // 数据为新的,但页面也是新的,数据与页面保持同步
            },
            beforeDestroy() {
                console.log('beforeDestroy')
                // console.log(this.n);
                // this.add()
                // debugger
                //销毁阶段触发
                // 点击销毁vm,能打印出n,调用了add方法,但页面不再更新,即到了这个阶段,
                //能够访问到数据,调用方法, 但所有对数据的修改不会再触发更新了。
                // 此时vm中的data methods 指令等都处于可用状态,马上要执行销毁过程,
                // 一般在此阶段:关闭定时器,取消订阅消息,解绑自定义事件等收尾操作

            },
            destroyed() {
                //销毁阶段触发
                console.log('destroyed')
            },
        })
        // vm.$mount("#root")
    </script>
</html>

常用的生命周期钩子:

1. mounted: 发送ajax请求、启动定时器、绑定自定义事件、订阅消息等 (初始化操作)

2. beforeDestroy: 清除定时器、解绑自定义事件、取消订阅消息等 (收尾工作)

关于销毁 Vue 实例

1. 销毁后借助 Vue 开发者工具看不到任何信息。

2. 销毁后自定义事件会失效,但原生 DOM 事件依然有效。
3. 一般不会在 beforeDestroy 操作数据,因为即便操作数据,也不会再触发更新流程了

vm的一生(vm的生命周期)∶
将要创建===>调用beforeCreate函数 创建完毕===>调用created函数。
将要挂载===>调用beforeMount函数
(重要)挂载完毕===>调用mounted函数 ============>【重要的钩子】
将要更新===>调用beforeUpdate函数
更新完毕===>调用updated函数
(重要)将要销毁===>调用beforeDestroy函数 ========>【重要的钩子】
销毁完毕===>调用destroyed函数

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值