一、入门
1.安装
安装vue.js有很多种方法,详见 Vue.js文档。这里我们采用npm命令来安装Vue。首先需要安装node.js,安装好之后打开cmd窗口,跳转到自己的项目文档下,输入npm install vue命令,来安装vue。
$ npm install vue
2.实例属性和方法
Vue.js 的核心是一个允许采用简洁的模板语法来声明式地将数据渲染进 DOM 的系统。一个 Vue 应用会将其挂载到一个 DOM 元素上 (对于这个例子是 #app) 然后对其进行完全控制。
当一个 Vue 实例被创建时,它将 data 对象中的所有的 property 加入到 Vue 的响应式系统中。当这些 property 的值发生改变时,视图将会产生“响应”,即匹配更新为新的值。
<body>
<div id="app">
<ul v-if="isShow"> //v-if:将根据表达式isShow的值(true 或 false)来决定是否插入ul元素
<li v-for="(item,index) in arr" key=`${index}_a`>
//v-for:基于源数据多次渲染元素或模板块
{{item}}
//{{}}小胡子语法:用于输出对象属性和函数返回值。
</li>
</ul>
<div v-else>11</div>
//v-else:前一兄弟元素必须有 v-if 或 v-else-if。
//isShow=false时,不插入ul元素,插入元素<div>11</div>
<input type="text" v-model="age">{{age}}
//v-model在表单控件或者组件上创建双向绑定。
<input type="text" :value="age" @input="fn"> {{age}}
//这种绑定方式与上一种相同
</div>
</body>
<script src="./vue.min.js"></script>
<script>
let vm = new Vue({
//每个 Vue 应用都需要通过实例化 Vue 来实现。
//Vue构造器中有一个el参数来确定元素, 用data定义属性,methods定义函数
el: "#app",
data: {
arr: ["a", "b", "c"],
isShow: true,
age: 10,
},
methods: {
fn(e) {
this.age = e.target.value;
}
}
})
</script>
3.指令
1)指令是带有v-前缀的特殊属性。指令用于在表达式的值改变时,将默写行为应用到DOM上。这里就不一一列举vue的指令,详细可见 Vue指令列表。
2)注意v-show和v-if的小区别:
v-show:根据表达式之真假值,切换元素的 display CSS property。
v-if:根据表达式的值的 truthiness 来有条件地渲染元素。在切换时元素及它的数据绑定 / 组件被销毁并重建。
v-show只是改变css的display属性,而v-if是决定要不要插入该元素。
二、自定义指令
这里给出一个例子,点击box区域内任何地方,都会显示日历控件。点击box区域外,日历控件则消失。
自定义v-outside绑定在box元素上。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.box {
width: 300px;
height: 400px;
background-color: aqua;
}
.date {
width: 100px;
height: 100px;
background-color: red;
}
</style>
</head>
<body>
<div id="app">
<div class="box" v-outside>
<input type="text">
<div class="date" v-if="isShow">
日历控件
<button>确定</button>
</div>
</div>
</div>
</body>
<script src="vue.min.js"></script>
<script>
let vm = new Vue({
el: "#app",
data: {
isShow: false
},
methods: {
blur() {
this.isShow = false;
},
focus() {
this.isShow = true;
}
},
directives: {
"outside": {
bind(el, binding, vnode) { //事件的绑定在这个钩子函数中处理
//el->box 为绑定的元素
//vnode为虚拟节点
//vnode.context 上下文 vue实例vm
el.fn = (e) => {
//e.target 目标元素
if (el.contains(e.target)) { //如果box包含目标元素
vnode.context.focus()
} else {
vnode.context.blur();
}
}
document.addEventListener('click', el.fn, false) //把绑定的函数放在el的自定义属性fn上
},
unbind(el) {
//事件的绑定移除
document.removeEventListener('click', el.fn, false);
}
}
}
})
//vm.$destory() //销毁实例
</script>
</html>
三、响应式原理
手写模拟vue响应式原理。
//数据改变时,视图更改
let oldArrayPrototype = Array.prototype;
let proto = Object.create(oldArrayPrototype); //组合式继承
['push', 'shift', 'unshift', 'splice'].forEach(method => {
//函数劫持,把函数进行重写,内部会继续调用老的方法
proto[method] = function() {
updateView();
oldArrayPrototype[method].call(this, ...arguments)
}
})
function observe(target) {
if (typeof target != 'object' || target == null) {
return target;
}
if (Array.isArray(target)) {
Object.setPrototypeOf(target, proto);
//target.__proto__ = proto;
for (let i = 0; i < target.length; i++) {
observe(target[i]);
}
}
//对对象中的每个属性进行监视
for (let key in target) {
defineReactive(target, key, target[key]);
}
}
function defineReactive(target, key, value) {
//响应式系统
observe(value); //递归调用 对对象进行拦截
Object.defineProperty(target, key, {
get() { //获取属性值
return value;
},
set(newVal) { //设置属性值
if (newVal !== value) {
observe(newVal);
updateView();
value = newVal;
}
}
})
}
function updateView() {
console.log('updataView')
}
//let data = { name: 'zf' } //主要依赖Object.defineProperty get set
//let data = { name: { m: { n: 20 } } }
let data = { name: [1, 2.3] }
observe(data);
data.name.push(4); //push shift unshift splice 。。。经过重写数组的方法
//data.name //get
//data.name = "zhufeng"; //调用set方法
/* data.name = { m: 30 };
data.name.m = 20; */
/* data.name.m = { n: 40 };
data.name.m.n = 30;
data.age = 10 //属性不存在 不会响应式 */