和阿牛一起冲Vue
🌕写在前面
🍊博客主页 :勇敢link牛牛
🎉欢迎关注:🔎点赞👍收藏⭐️留言📝
🌟本文由 勇敢link牛牛 原创,CSDN首发!
📆首发时间:🌹2022年8月29日🌹
🆕最新更新时间:🎄2022年8月29日🎄
✉️愿你熬过万丈孤独,藏下星辰大海!
📠参考书籍:📚《Vue2》
🙏作者水平很有限,如果发现错误,请留言轰炸哦!万分感谢感谢感谢!
Vue基础--Vue模板语法-vue数据绑定原理-插值语法-指令语法
Vue基础
一、Vue基础
1.1、介绍
Vue.js是一套构建用户界面的渐进式框架。Vue 采用自底向上增量开发的设计。Vue 的核心库只关注视图层,它不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与单文件组件和 Vue 生态系统支持的库结合使用时,Vue 也完全能够为复杂的单页应用程序提供驱动。
- 渐进式:一步一步,不是说你必须一次把所有的东西都用上
- 自底向上设计:是一种设计程序的过程和方法,就是先编写出基础程序段,然后再逐步扩大规模、补充和升级某些功能,实际上是一种自底向上构造程序的过程。
Vue从设计角度来讲,虽然能够涵盖这张图上所有的东西,但是你并不需要一上手就把所有东西全用上,都是可选的。声明式渲染和组件系统是Vue的核心库所包含内容,而路由、状态管理、构建工具都有专门解决方案。这些解决方案相互独立,你可以在核心的基础上任意选用其他的部件,不一定要全部整合在一起。
1.2、声明式渲染和组件化
💖声明式渲染
Vue.js 的核心是一个允许采用简洁的模板语法来声明式的将数据渲染进 DOM 的系统
<div id="app">
<!-- 渲染 Hello Vue -->
{{ message }}
</div>
#js
var vm = new Vue({
// 选择真实html中的 选择器
el: '#app',
// 数据源 -- 声明模板变量所在的位置
data(): {
// 返回一个对象
return {
// 声明一下变量
message: 'Hello Vue!'
}
}
})
💖 组件化应用构建
组件系统是 Vue 的另一个重要概念,因为它是一种抽象,允许我们使用小型、独立和通常可复用的组件构建大型应用。几乎任意类型的应用界面都可以抽象为一个组件树。
{
tag:div
children:[
{
tag:div
children:[
…
]
}
]
}
1.3、MVVM模式
MVVM是Model-View-ViewModel的简写。它本质上就是MVP 的改进版。MVVM 就是将其中的View 的状态和行为抽象化,让我们将视图 UI 和业务逻辑分开。当然这些事 ViewModel 已经帮我们做了,它可以取出 Model 的数据同时帮忙处理 View 中由于需要展示内容而涉及的业务逻辑。
vue使用MVVM响应式编程模型,避免直接操作DOM , 降低DOM操作的复杂性。
- MVVM:页面输入改变数据,数据改变影响页面数据展示与渲染
- M(model):普通的javascript数据对象
- V(view):前端展示页面
- VM(ViewModel):用于双向绑定数据与页面,其实就是vue的实例
优点:
- 低耦合。视图(View)可以独立于Model变化和修改,一个ViewModel可以绑定到不同的View上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变。
- 可重用性。你可以把一些视图逻辑放在一个ViewModel里面,让很多view重用这段视图逻辑。
- 独立开发。开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员可以专注于页面设计。
- 可测试。界面素来是比较难于测试的,而现在测试可以针对ViewModel来写。
二、vue基础使用
2.1、传统dom操作
使用js对html页面结构中的指定的区域输出数据
<div></div>
<input type="text" name="" id="">
<script>
var input = document.querySelector("input");
input.addEventListener("input",function(e){
var div = document.querySelector("div");
div.innerHTML = this.value.trim();
});
</script>
<body>
<div></div>
<input type="text" name="" id="">
<script>
/* 数据源 */
var data ={
message :"我爱你"
}
var target = {};/* 做个代理中转 目标数据*/
/* 做个属性侦听改变侦听 响应数据*/
Object.defineProperty(target,"message",{
get(){
// return data.message;
return Reflect.get(data,"message")
},
set(newValue){
data.message = newValue;
document.querySelector("div").textContent = newValue;
}
})
/* 初步把数据源的值,分别传给div和input */
var input = document.querySelector("input");
input.value =target.message;
document.querySelector("div").textContent = target.message;
/* 间接代理服务 */
input.addEventListener("input",function(e){
target.message = this.value.trim();
});
</script>
</body>
2.2、使用vue实现
在html页面中使用好vue需要完成如下步骤即可
- 引入vue.js文件
- 定义给vue.js管理的dom元素(给div定义一个ID)
- 创建一个 Vue 的实例,并声明要渲染的数据源
- 在给定的dom元素容器内,绑定数据源中变量名称
{{变量名}}
Vue参数对象属性
- el:元素挂载的位置,值可以是CSS选择器或DOM元素
- data:模型数据,值是一个对象
插值表达式
将数据填充到HTML标签中
支持基本的JavaScript计算操作,例如算术运算、字符串拼接等
下载vue.js
在html中引入vue.js文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>vue学习使用</title>
<!-- 第1步: 引入vue库文件 -->
<script src="./js/vue.js"></script>
</head>
<body>
<!-- 第2步:根容器,js对象转为真实的html后,在放在网页的什么位置中 挂载点 -->
<!-- 挂载点可以写多个,但是在工作中一般只有一个 -->
<div id="app">
<!-- 第4步:使用 -->
<!-- 模板语法 插值表达式 mustcache模板名称[小胡子语法] -->
<!-- title它就是在vue实例化配置data属性中的数据 -->
<h3>{{ title }}</h3>
<!-- <input type="text" id="setTitle"> -->
<!-- v-model,语法糖 -->
<input type="text" v-model="title">
</div>
<!-- 第3步:实例化vue -->
<script>
const vm = new Vue({
// vue处理完成数据展示在页面中的挂载点dom
el: '#app',
// vue2中,唯一一个地方可以把data写成对象地方,就是在实例化vue类时
// 声明vue要渲染的数据
data: {
title: 'hello vue'
}
})
console.log(vm.title)
// document.getElementById('setTitle').value = vm.$data.title
// document.getElementById('setTitle').oninput = e => vm.$data.title = e.target.value.trim()
</script>
</body>
</html>
<div id="app">
<div class="div1">{{message}}</div>
<input type="text" v-model="message">
</div>
<script>
var vm = new Vue({
el:"#app",
data:()=>{
return{
message:"我爱你"
}
}
})
</script>
2.3、为什么要使用虚拟Dom
用传统的开发模式,原生JS或JQ操作DOM时,浏览器会从构建DOM树开始从头到尾执行一遍流程。在一次操作中,我需要更新10个DOM节点,浏览器收到第一个DOM请求后并不知道还有9次更新操作,因此会马上执行流程,最终执行10次。
例如,第一次计算完,紧接着下一个DOM更新请求,这个节点的坐标值就变了,前一次计算为无用功。计算DOM节点坐标值等都是白白浪费的性能。即使计算机硬件一直在迭代更新,操作DOM的代价仍旧是昂贵的,频繁操作还是会出现页面卡顿,影响用户体验。
Web界面由DOM树(树的意思是数据结构)来构建,当其中一部分发生变化时,其实就是对应某个DOM节点发生了变化,虚拟DOM就是为了解决浏览器性能问题而被设计出来的。之前,若一次操作中有10次更新DOM的动作,虚拟DOM不会立即操作DOM,而是将这10次更新的diff内容保存到本地一个JS对象中,最终将这个JS对象一次性attch到DOM树上,再进行后续操作,避免大量无谓的计算量。所以,用JS对象操作虚拟DOM节点的好处:页面的更新可以先全部反映在JS对象(虚拟DOM)上,操作内存中的JS对象的速度显然要更快,等更新完成后,再将最终的JS对象映射成真实的DOM,交由浏览器去绘制,从而提高性能。
2.4、vue devtools工具安装
通过chrome中的谷歌插件商店安装Vue Devtools工具,此工具帮助我们进行vue数据调试所用,一定要安装。
https://chrome.google.com/webstore?utm_source=chrome-ntp-icon
https://chrome.zzzmh.cn/
在vscode中安装插件
2.5、Vue是如何实现数据绑定的原理
当把一个普通的JavaScript对象传给Vue实例的data选项,Vue将遍历此对象所有的属性,使用Object.defineProperty(vue2.x)
,vue3.x中使用了Proxy类把这些属性全部转为getter/setter(数据劫持)。在属性被访问和修改时通知变化。每个组件实例都有相应的 watcher 实例对象,它会在组件渲染的过程中把属性记录为依赖,之后当依赖项的 setter 被调用时,会通知 watcher 重新计算,从而致使它关联的组件得以更新。
模拟vue实现数据响应式
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="root">
<h3 v-text="title"></h3>
<hr>
<input type="text" v-model='title'>
</div>
<script>
// 数据劫持
let data = {
title: '我是一个标题',
};
// 观察数据
observe(data)
// 给input绑定事件
document.querySelector('[v-model]').addEventListener('input', function () {
let key = this.getAttribute('v-model')
data[key] = this.value.trim()
})
function observe(target) {
if (!isObject(target)) return;
for (let key in target) {
defineReactive(target, key, target[key])
}
}
// 数据劫持
function defineReactive(target, key, value) {
Object.defineProperty(target, key, {
get() {
console.log('get')
return value
},
set(v) {
if (v != value) {
value = v
console.log('set')
// 更新视图
updateView(value, key)
}
}
})
}
function updateView(value, key) {
document.querySelectorAll('[v-text]').forEach(node => {
let attrValue = node.getAttribute('v-text')
if (key === attrValue) {
if (node.nodeName === 'INPUT') {
node.value = value;
} else {
node.innerHTML = value;
}
}
})
}
function isObject(target) {
return {}.toString.call(target) === '[object Object]'
return {}.toString.call(target) === '[object Array]'
}
</script>
</body>
</html>
2.6、Diff比对
Vue中当数据发生改变的时候,对应监听的set方法会执行,调用数据中的Dep.notify方法通知所有的订阅者,订阅者就会通过patch函数对比新旧虚拟节点是否一样,如果用新的虚拟节点则整个替换老节点,如果不是使用新节点,则根据子节点情况来进行同层比较。
patch主要做4个判断
①、没有新节点,直接触发旧节点的destory钩子,进行销毁。
②、没有旧节点,此时根本不需要比较了,直接全部都是新建
③、旧节点和新节点一样时,直接调用 patchVnode 去处理这两个节点
④、旧节点和新节点自身不一样,当两个节点不一样的时候,直接创建新节点,删除旧节点
没有key的比较
比对时有key值标识
key值的作用:在使用diff算法比较新旧dom树的时候,可以更准确更快得找到oldDom树中对应的节点。(利用key的唯一性生成map对象来获取对应节点,比遍历方式更快)
patchVnode的位置
三、模板语法
3.1、插值表达式
插值表达式是vue框架提供的一种在html模板中绑定数据的方式,使用{{变量名}}
方式绑定Vue实例中data
中的数据变量。会将绑定的数据实时的显示出来。
3.2、支持写法
{{变量、js表达式、三目运算符、方法调用等}}
<div id="app">
vue模板的表达式为 {{ 变量 }}
表达式中不能定义变量或函数,也不可以写if条件或循环
<!-- 绑定变量 -->
<div>{{ title }}</div>
进行计算 vue模板中不太建议在里面写运算
<div>{{ 1+1 }}</div>
调用函数或方法
<div>{{sum()}}</div>
调用js内置方法 不太推荐
<div>{{ '电饭锅是'.substr(0,3) }}</div>
三目运算
<div>{{ age<20?'未成年':'成年' }}</div>
</div>
<script src="./vue.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
age: 18,
title: 'hello vue',
函数可以写在data中的,但是不太建议
data中的数据都会被劫持,但是函数是没有必须劫持的
vue为了提升性能,新增一个配置选项,用于定义函数方法method,它不会被劫持
},
methods: {
sum() {
return 1 + 2
}
}
})
</script>
注:{{}}
括起来的区域,就是一个就是js语法区域,在里面可以写部份的js语法。不能写 var a = 10; 分支语句 循环语句
3.3、指令
指令(Directives)
就是vue给html标签提供的一些自定义属性,这样属性都是带有 v-
前缀的特殊属性。指令特性的值预期是单个JS表达式(v-for是例外情况)。
指令的职责是,当表达式的值改变时,将其产生的连带影响,响应式地作用于DOM。
vue中的指令可以操作真实的dom的
指令作用:
- 操作dom(权限限制)
- 表单验证
v-html
:解析html指令 注:尽量避免使用,容易造成危险 (XSS跨站脚本攻击)
- 解析字符串中的html标签,解析输出,一般用于在后台添加的富文本内容,在前台显示所用
v-text
:输出文本信息二者都有消除闪白、闪动的能力
- 转义输出,为了解决早期没有使用工程化时,直接script引入vue,进行使用时有花括号闪现问
题
<div id="app">
<!-- 转义html -->
<h3>{{html}}</h3>
<!-- 解析 html -->
<div v-html="html"></div>
<!-- 转义html -->
<div v-text="html"></div>
</div>
<script src="./vue.js"></script>
<script>
new Vue({
el: '#app',
data: {
html:'<a href="http://www.baidu.com/">百度一下</a>'
}
})
</script>
四、常用指令
指令扩展了html标签的功能、大部分的指令的值是js的表达式,取代了DOM操作。
4.1、v-cloak指令
解决浏览器在加载页面时因存在时间差而产生的“闪动”问题
v-html或v-text
它们能够解决闪现,但是有的时候,一些项目在前期没有用它,这时候如果全部去修改,工程量有点大,可以用此指令
# css中设置
[v-cloak] {
display: none;
}
# html
<div v-cloak>
{{ message }}
</div>
4.2、v-pre指令
此指令一个性能优化的指令,使用它,可以提升性能。
跳过这个元素和它的子元素的编译过程,跳过大量没有指令的节点会加快编译。
<span v-pre>不需要编译,直接可以运行</span>
ul中的数据,它是一个固定的,没有通过vue中的数据来渲染
无需把它们转为虚拟dom,直接存储html,在显示直接输出
<ul v-pre>
<li>首页</li>
<li>列表</li>
</ul>
4.3、v-once指令
只渲染元素和组件一次,之后元素和组件将失去响应式功能
对于某些特定数据,只绑定一次 性能优化
<div id="app">
<h3>{{message}}</h3>
<hr>
<!-- 动态修改message值,此绑定将不会发生改变 -->
<div v-once>{{message}}</div>
</div>
<script src="./vue.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
message: '你好世界'
}
})
</script>
4.4、v-if和v-show
根据表达式的布尔值(true/false)
进行判断是否渲染该元素
v-if
是对dom元素的移除和创建
v-show
是通过修改标签的display值
v-if
:如果条件不成立,则不创建或删除已创建元素,初始化它的性能更高一些,如果频繁切换则性能低一些,权限显示相关,建议用v-ifv-show
:如果条件不成立,通过样式来隐藏,初始化它的性能低一些,如果频繁切换则性能更好一些
<div id="app">
<!-- 如果isShow的值是true ,就显示p标签 -->
<p v-if="isShow">我是p标签中的内容</p>
<p v-show="isShow">我是p标签中的内容</p>
</div>
<script src="./vue.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
isShow: true
}
})
</script>
注:v-if
有更高的切换开销,而 v-show
有更高的初始渲染开销。
因此,如果需要非常频繁地切换,则使用 v-show
较好;如果在运行时条件很少改变,则使用 v-if
较好。
多条件判断
v-if … v-else-if v-else
必须连在一起使用。
<div id="app">
<div v-if="age<10">儿童</div>
<div v-else-if="age<20">少年</div>
<div v-else>老年</div>
</div>
4.5、v-bind指令
动态地绑定一个或多个属性
<!-- v-bind绑定href属性值 -->
<a v-bind:href='url'>跳转</a>
<!-- v-bind绑定href属性值(简写形式) -->
<a :href='url'>跳转</a>
4.6、v-for
根据一组数组或对象的选项列表进行渲染。
v-for指令需要使用 (item,index) in 数组或对象 形式的特殊语法,同时还需要指定key值,key的作用在vue进行新旧数据比对渲染页面里,如果有key值会提升比对性能,加快渲染,key使用唯一的值来赋值。
<div id="app">
<ul>
<!-- 数组循环 -->
<li v-for="u in users" :key="u">{{ u }}</li>
</ul>
<hr>
<ul>
<!-- 对象循环 -->
<li v-for="(item,key,index) in obj">{{ index }} -{{ key }} - {{item}}</li>
</ul>
</div>
<script src="./vue.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
users: [1, 2, 3],
obj: {
username: 'zhangsan',
age: 28,
gender: 'male'
}
}
})
</script>
4.7、v-on
绑定事件监听器(事件绑定)
<!-- 常规写法 -->
<button v-on:click="事件实现方法()"></button>
<!-- 缩写 -->
<button @click="事件实现方法()"></button>
# 绑定好事件实现方法后需要在Vue对象中的methods对象中实现对应的绑定方法
methods: {
functionName(arg1,arg2,arg3,...){
// something to do
},
....
}
事件处理函数传参 -- 事件对象--- event对象
<!-- 事件处理函数调用:直接写函数名 -->
<button @click="say"></button>
<!-- 事件处理函数调用:常规调用 -->
<button @click="say($event)"></button>
# 注:如果没有参数时,可以不写小括号,默认就会把event事件对象绑定到实现的方法中,如果需要传参数时,则通过 $event 来手动传递到实现的方法中
事件修饰符
用来处理事件的特定行为
<!-- 阻止冒泡 -->
<button @click.stop="doThis"></button>
<!-- 阻止默认行为 -->
<a @click.prevent="doThis"></a>
<!-- 只执行一次 -->
<div @click.once="incr()">自增一下</div>
<!-- 绑定的元素本身触发时才触发回调 -->
<ul @click.self="incr()">
<li>你好世界</li>
</ul>
<!-- 串联修饰符 -->
<button @click.stop.prevent="doThis"></button>
按键修饰符
在监听键盘事件时,我们经常需要检查详细的按键。Vue 允许为 v-on 在监听键盘事件时添加按键修饰符。
<!-- 只有在 `key` 是 `Enter` 回车键的时候调用 -->
<input @keyup.enter="submit">
<!-- Ctrl + Click -->
<div @click.ctrl="doSomething">Do something</div>
你还可以通过全局 config.keyCodes 对象自定义按键修饰符别名:
// 可以使用 `v-on:keyup.f1`
Vue.config.keyCodes = {f2:113}
<input @keyup.f2="add()" value="aaaa">
实现v-show的动画切换过程。
transition过渡属性对于页面元素的显隐并没有提供过渡方法!这就导致元素从display:none到display:block的过程中,瞬发没有过渡效果。这里我们可以换个思路,用透明度解决这个问题,通过透明度opacity属性从0到1的变化模拟元素的显隐,并且过渡属性transition是提供对透明度opacity属性的过渡的。
暂时没实现过渡效果。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>切换1</title>
<script src="./vue.js"></script>
<style>
*{
padding: 0;
margin: 0;
}
.div0{
position: relative;
height: 225px;
padding: 0;
margin: 0 auto;
border: 1px solid rebeccapurple;
}
div{
width: 200px;
height: 200px;
}
.div1{
float: left;
position: absolute;
background-color: rgb(239, 85, 85);
transition:all 1s;
Opacity:1;
}
.div2{
float: left;
position: absolute;
background-color: rgb(89, 74, 110);
transition:display 2s;
}
.div3{
float: left;
position: absolute;
background-color: rgb(93, 36, 36);
/* transition:Opacity 2s; */
}
</style>
</head>
<body>
<div id="app" class="div0">
<button @click="isShow($event)" id="Bn1">1号</button><button @click="isShow($event)" id="Bn2">2号</button><button @click="isShow($event)" id="Bn3">3号</button>
<div class="div1" v-show="buttName =='Bn1'">{{message.substr(0,1)}}</div>
<div class="div2" v-show="buttName =='Bn2'">{{message.substr(1,1)}}</div>
<div class="div3" v-show="buttName =='Bn3'">{{message.substr(2,3)}}</div>
</div>
<script>
var vm = new Vue({
el:"#app",
data:function(){
return {
message:"我爱你",
buttName:"Bn1"
}
},
methods:{
isShow(e){
console.log(e.target.id);
this.buttName = e.target.id
console.log(e.target);
}
}
})
</script>
</body>
</html>