目录
目标
一、Vue基础
1.1、介绍
1.2、声明式渲染和组件化
1.3、MVVM模式
二、vue基础使用
2.1、传统dom操作
2.2、使用vue实现
2.3、vue devtools工具安装
2.4、Vue实现数据绑定的原理
三、模板语法
3.1、插值表达式
3.2、指令
四、常用指令
目标
- 在网页中实例化vue对象
- 知道vue数据绑定原理
- 熟练使用插值表示式
- 掌握vue指令
一、Vue基础
1.1、介绍
官网:https://cn.vuejs.org/
Vue (读音 /vju?/,类似于?view)? , Vue.js是一套构建用户?界面?的 渐进式框架 。Vue 采用自底向上增量开发的设计。Vue 的核心库?只关注视图层?,它不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与?现代化的工具链?以及各种?支持类库?结合使用时,Vue 也完全能够为复杂的单页应用程序提供驱动。( spa 单页面应用,所有的显示都在一个页面当中)
渐进式:一步一步,不是说你必须一次把所有的东西都用上
自底向上设计:是一种设计程序的过程和方法,就是先编写出基础程序段,然后再逐步扩大规模、补充和升级某些功能,实际上是一种自底向上构造程序的过程。
?
Vue从设计角度来讲,虽然能够涵盖这张图上所有的东西,但是你并不需要一上手就把所有东西全用上,都是可选的。声明式渲染和组件系统是Vue的核心库所包含内容,而路由、状态管理、构建工具都有专门解决方案。这些解决方案相互独立,你可以在核心的基础上任意选用其他的部件,不一定要全部整合在一起。
?
1.2、声明式渲染和组件化
Vue.js 的核心是一个允许采用简洁的?模板语法?来声明式的将数据渲染进 DOM 的系统
# html
<div id="app">
??<!-- 渲染 Hello Vue -->
??{{ message }}
</div>
# js
var vm = new Vue({
??el: '#app',
??// 数据源
??data: {
// 声明一下变量
????message: 'Hello Vue!'
??}
})
组件系统是?Vue 的另一个重要概念,因为它是一种抽象,允许我们使用小型、独立和通常可复用的组件构建大型应用。几乎任意类型的应用界面都可以抽象为一个组件树。
1.3、MVVM模式
MVVM是Model-View-ViewModel的简写。它本质上就是MVC 的改进版。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页面结构中的指定的区域输出数据
传统dom操作数据显示 :?
<div>
<h3 id="app">abcd</h3>
<input type="text" id="msg">
</div>
<script>
var data = {
username: '张三'
}
document.querySelector('#app').innerHTML = data.username
// document.querySelector('#app').textContent = data.username
// 查看当前dom的类型 1元素 3文本
// console.log(document.querySelector('#app').nodeType)
// console.log(document.querySelector('#app').firstChild.nodeType)
document.querySelector('#msg').value = data.username
document.querySelector('#msg').addEventListener('input', function () {
// 让使用数据源的标签更新视图
document.querySelector('#app').innerHTML = this.value.trim()
// 更新数据源
data.username = this.value.trim()
})
</script>
?
2.2、使用vue实现
在html页面中使用好vue需要完成如下步骤即可
- 引入vue.js文件
- 定义给vue.js管理的dom元素(给div定义一个ID)
- 创建一个?Vue 的实例,并声明要渲染的数据源
- 在给定的dom元素容器内,绑定数据源中变量名称{{变量名}}
????el:元素挂载的位置,值可以是CSS选择器或DOM元素
????data:模型数据,值是一个对象
????将数据填充到HTML标签中
????支持基本的JavaScript计算操作,例如算术运算、字符串拼接等
?
下载vue.js
https://cn.vuejs.org/v2/guide/
?
<!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>
</head>
<body>
<!-- 下面这是能在界面看见的部分就是 视图 部分 -->
<!-- 被 Vue 管理 -->
<div id="app">
<!-- {{ 双花括号之间不能有空格 , 需要紧挨在一起 }} -->
<h1> {{message}} </h1>
<h2>{{name}}</h2>
</div>
<!-- 不被 Vue 管理 -->
<div>{{message}}</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
<script>
/*
Vue 最大的特点 : 数据双向绑定
这个双向绑定指的是谁和谁绑定的呢?? => 数据 和 视图
( 操作 数据 , DOM 也发生了变化 , input 输入框把 数据 告诉 app.message 这个属性
(由Vue 的实例 , 将上面的这一个 h1 进行了修改) )
1. DOM 将数据传递给 Vue 实例
2. Vue 实例的属性会传递给 DOM
*/
// let(变量) / const(常量)
// 编程范式 : 声明式编程 ( 当我这个实例去帮我管理这个 div 的时候 ,
//你只需要告诉我这里写的什么东西就可以了 , 你只需要声明我这里需要显示什么东西就可以了 , 至于它内部如何处理的 , 无需知晓 )
// 它的好处 : 可以真正做到数据和我们的界面完全分离 , 以后并不需要再通过代码去创建元素了
// 创建 vue 实例
const app = new Vue({
// vue 实例的配置项:
// element , vue 实例控制这一块 dom
el: '#app', // 用于挂载要管理的元素
// vue 实例的属性
// data 内部的属性和属性值就是 vue 实例的属性和属性值
// 下面 data 这一部分就是 数据
data: { // 定义数据
message: "hello Vue",
name: '小灰狼'
}
});
console.log(app.message);
// 响应式: 就是当数据发生改变的时候, 界面会自动发生一些响应, 会跟着自动更改
// 原始 js 的做法 : ( 编程范式 : 命令式编程 )
// =>( 就是一步一步告诉你怎么做, 需要你每一步都指定的非常清除 , 他才知道怎么做 )
// 1. 创建 div 元素 , 设置 id 属性
// 2. 定义一个变量叫 message
// 3. 将 message 变量放在前面的 div 元素中显示
// 4. 修改 message 的数据: 今天下雨天气 !
// 5. 将修改后的数据再次替换到 div 元素
</script>
</body>
</html>
通过 chrome 中的谷歌插件商店安装 Vue Devtools 工具,此工具帮助我们进行 vue 数据调试所用,一定要安装。
https://chrome.google.com/webstore?utm_source=chrome-ntp-icon
https://chrome.zzzmh.cn/
?
2.4、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>Vue实现数据绑定的原理</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 target !== null && typeof target === 'object' && !(target instanceof Array)
// return Object.prototype.toString.call(target) === '[object Object]'
return ({}).toString.call(target) === '[object Object]'
}
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>数据劫持</title>
</head>
<body>
<div id="app">
<h3 v-text="msg"></h3>
<h3 v-text="age"></h3>
<hr>
<input type="text" v-model="msg">
</div>
<script>
var data = {
msg: '你好mvvm',
age: 20,
user: { id: 1 },
user_id: 1,
user_name: 'aa'
}
// let target = { id: 100 }
// 冻结对象 对象就没有办法修改了
// target = Object.freeze(target)
// target.id = 200
// let target = {}
// 数据劫持
observe(data)
// 模板编译
compileRender(data)
function observe(target) {
// 只劫持json对象
if (Object.prototype.toString.call(target) != '[object Object]') return;
// 遍历
for (let key in target) {
defineRactive(target, key, target[key])
}
}
function defineRactive(target, key, value) {
observe(value)
/* if (Object.prototype.toString.call(value) == '[object Object]') {
observe(value)
return;
} */
// 劫持当前的对象
// defineProperty 它只能对对象中的属性进行劫持,不能劫持数组
Object.defineProperty(target, key, {
// 获取器
get() {
console.log('get')
return value
},
// 修改器
set(newV) {
if (newV != value) {
console.log('set')
value = newV
// 通知模板编译一下
compileRender(target)
}
}
});
}
function compileRender(target) {
// 模板编译
// 文本
document.querySelectorAll(`[v-text]`).forEach(node => {
// 要去数据源中查找的数据key
let key = node.getAttribute('v-text')
let value = target[key] || 0
node.innerHTML = value
})
// 输入框
document.querySelectorAll(`[v-model]`).forEach(node => {
let key = node.getAttribute('v-model')
let value = target[key] || 0
node.value = value
// 绑定一个事件
node.addEventListener('input', function () {
target[key] = this.value.trim()
})
})
}
</script>
</body>
</html>
相关知识点 :??
let target = { id: 100 } 冻结对象 , 对象就没有办法修改了 target = Object.freeze( target ) target.id = 200?
? // dom操作在内存中完成 =>?文档碎片 --> 在内存在存储,不会在界面中渲染,通过 appendChild 渲染到视图中 语法 :? document.createDocumentFragment( )?
作用 : 可以承载节点 , 当把文档碎片插入页面时 , 文档碎片不进入页面 , 只有文档碎片里面承载的内容进入页面 ( 往内存里写入东西比往页面里写入东西会更快 )?
( 1 ) 数据劫持-跳转链接? :??数据劫持 _ 简述版
( 2 ) 观察者模式 : 设计模式
自己来实现一个 _ MVVM :?
<!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>实现一个 _ MVVM</title>
<script src="./js/myVue.js"></script>
</head>
<body>
<div id="app">
<h3>{{ title }}</h3>
<input type="text" v-model='title'>
</div>
<script>
const vm = new Vue({
el: '#app',
data: {
title: '你好MVVM'
}
})
</script>
</body>
</html>
class Vue {
constructor(options) {
// 挂载点的 dom
this.$el = document.querySelector(options.el)
// 数据
this.$data = options.data
// 数据劫持
observe(this.$data)
//模板编译
compileTemplate(this.$el, this)
}
}
// 观察仓库
class Dep {
constructor() {
// 观察者队列
this.subscribes = []
}
// 添加观察者
addSub(watcher) {
this.subscribes.push(watcher)
}
// 通知更新
notify() {
this.subscribes.forEach(watcher => watcher.update())
}
}
// 观察者
class Watcher {
constructor(vm, key, node) {
// 仓库对象的静态属性来保存当前watcher实例对象
Dep.target = this;
this.$vm = vm;
this.$key = key;
this.$node = node;
// 触发数据劫持中的get方法,此时Dep.target的值为当前对象
this.getValue();
// 设置为null,为下次new Watcher准备
Dep.target = null;
}
getValue() {
// 触发get
this.$value = this.$vm.$data[this.$key]
}
update() {
// 获取一下最新数据
this.getValue();
if (this.$node.nodeType === 1) {
if (this.$node.nodeName == 'INPUT') {
this.$node.value = this.$value
} else {
this.$node.innerHTML = this.$value
}
} else if (this.$node.nodeType === 3) {
this.$node.textContent = this.$value
}
}
}
// 模板编译
function compileTemplate(el, vm) {
// dom操作在内存中完成
// 文档碎片 --> 在内存在存储,不会在界面中渲染,通过appendChild渲染到视图中
let fragment = document.createDocumentFragment()
let childNode;
while (childNode = el.firstChild) {
// 渲染模板
compileRender(childNode, vm)
// 把得到的dom对象放到fragment中,此时dom会删除
fragment.appendChild(childNode)
}
// 处理完成后,放到视图中
el.appendChild(fragment)
}
// 编译视图显示
function compileRender(node, vm) {
// 判断当前节点的类型 1元素,3文本
if (node.nodeType === 1) {
// 得到元素所有的属性集合
// [...node.attributes].forEach(attrObj => {
// console.log(attrObj.name, attrObj.value);
[...node.attributes].forEach(({ name, value }) => {
// 只关心v-开头属性
if (/^v-/.test(name)) {
if (name === 'v-model') { // 针对于input输入框
node.value = vm.$data[value]
// 观察者
new Watcher(vm, value, node)
// 绑定事件
node.addEventListener('input', function () {
vm.$data[value] = this.value.trim()
});
} else {
// 观察者
new Watcher(vm, value, node)
node.innerHTML = vm.$data[value]
}
}
});
// 问一下有没有子元素了
node.childNodes.forEach(child => compileRender(child, vm))
} else if (node.nodeType === 3) { // 文本节点
// 内容
let cnt = node.textContent
// 匹配只有{{}}才进行处理
let preg = /\{\{\s*(\w+)\s*\}\}/
// 替换
cnt = cnt.replace(preg, (a0, a1) => {
// 观察者
new Watcher(vm, a1, node)
return vm.$data[a1]
})
node.textContent = cnt
}
}
// ------------------- 数据劫持
// 监听数据源
function observe(target) {
// 只劫持json对象
if (Object.prototype.toString.call(target) != '[object Object]') return;
// 遍历
for (let key in target) {
defineRactive(target, key, target[key])
}
}
// 实现劫持
function defineRactive(target, key, value) {
observe(value)
let dep = new Dep()
Object.defineProperty(target, key, {
// 获取器
get() {
if (Dep.target) {
// 添加了观察者到通知队列中
dep.addSub(Dep.target)
}
return value
},
// 修改器
set(newV) {
if (newV != value) {
value = newV
dep.notify()
}
}
});
}
三、模板语法
3.1、插值表达式
插值表达式是vue框架提供的一种在html模板中绑定数据的方式,使用{{变量名}}方式绑定Vue实例中data中的数据变量。会将绑定的数据实时的显示出来。
# 支持写法
{{ 变量、js 表达式、三目运算符、方法调用 等 }}
<div id="app">
<h3>{{ name }}</h3>
<h3>{{ name + '--好的' }}</h3>
<h3>{{ 1 + 1 }}</h3>
<!-- 使用函数 -->
<h3>{{ title.substr(0,6) }}</h3>
<!-- 三目运算 -->
<h3>{{ age>22 ? '成年':'未成年' }}</h3>
</div>
<script src="./vue.js"></script>
<script>
??new Vue({
????el: '#app',
????data: {
??????title: '我是一个标题,你们看到没有',
??????name: '张三',
??????age: 20
????}
??})
</script>
注:{{ }}括起来的区域,就是一个就是 js 语法区域,在里面可以写部份的 js 语法。不能写?var a = 10; 分支语句 循环语句
<!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>
<!-- 引入vue.js类库 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
</head>
<body>
<!-- 指定vue要去解析的模板挂载点 -->
<div id="app">
<div>{{ msg }}</div>
<div>{{ msg+'123' }}</div>
<div>{{ 1 + 2 }}</div>
<div>{{ msg.substr(0,2) }}</div>
<div>{{ fn() }}</div>
<div>{{ age<18?'儿童':'成年' }}</div>
<!-- 这样不可以,不能赋值,只能使用 -->
<!-- <div>{{ var a=10 }}</div> -->
</div>
<script>
// 实例化Vue
const vm = new Vue({
el: '#app',
data: {
msg: '你好Vue',
age: 18
/* fn() {
return 'hello fn'
} */
},
// vue专门用来给写方法所用配置
methods: {
/* fn() {
return 'hello fn'
} */
// 在vue中的methods方法,定义时不能使用箭头函数,使用后this指向就会不对
// 方法里面定义函数建议使用箭头函数,保持this指向
fn: function () {
// return 'hello fn@' + this.$data.msg
setTimeout(() => {
console.log(this)
}, 3000);
return 'hello fn@' + this.msg
}
// fn: () => {
// // this undefined => 现在没有开启强制模式
// // 在vue.js引入的方案中它是window
// // console.log(this)
// return 'hello fn@' + this.msg
// }
}
})
</script>
</body>
</html>
?
3.2、指令
指令(Directives)就是vue给html标签提供的一些自定义属性,这样属性都是带有?v-?前缀的特殊属性。指令特性的值预期是单个JS表达式(v-for是例外情况)。指令的职责是,当表达式的值改变时,将其产生的连带影响,响应式地作用于DOM。
指令作用:
v-html 解析html指令 ?注:尽量避免使用,容易造成危险?(XSS跨站脚本攻击)
v-text 输出文本信息
-----------------------------------------------------
<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操作。
|