前端的MVVM概念今年来也算是如火如荼,了解完 MVVM的概念,也该找个去尝试下
首先我先试了下 国内小而美的 VUE
- 试着照着文档敲出入门文件,内容都在注释里
<!doctype html>
<html>
<head>
<title>VUE 入门</title>
<style>
body{display:flex;flex-wrap: wrap;align-items:flex-end;}
body>div{box-shadow:0 0 3px #000;margin:8px;padding:8px;}
</style>
</head>
<body>
<!-- 插值也能插在 dom 的属性里 -->
<div id="app" content="{{message}}">
<h4>基础插值</h4>
<!-- 基础文本插值 -->
<p>{{message}} </p>
<!-- 单次插值,之后不再变化 -->
<p>{{* message}} </p>
<!-- 可用来输出HTML 只对可信内容进行 HTML 插值 -->
<p>{{{message}}}</p>
<!-- 双向绑定 -->
<!-- 按键修饰符 可绑定按钮事件 也可使用别名以及自己配置别名 -->
<input type="text" v-model="message" v-on:keyup.13="show" />
</div>
<!-- 渲染列表 -->
<div class="list">
<h4>列表渲染</h4>
<ul>
<!-- 直接使用表达式 -->
<input type="text" v-model="query" placehorder="筛选">
<li v-if="item.show" id='item{{$index + 1}}' v-for="item in items | filterBy query" track-by="id">
<h4>{{item.title}}</h4>
<p>{{item.content}}</p>
<!-- 遍历对象,不同JS引擎结果不一样 -->
<h5 v-for="value in item">
{{$key}} : {{value}}
</h5>
</li>
</ul>
<!-- v-for 可接收一个整数 -->
<span v-for="n in 10">{{n + 1}}</span>
</div>
<!-- 绑定方法 -->
<div class="show">
<h4>按钮事件绑定</h4>
<span>{{message}}</span><br/>
<input type="text" v-model="name" /><br/>
<!-- 监听事件 -->
<!-- 可通过内联JavaScript语句传值. $event -->
<a href="http://www.baidu.com" v-on:click="showMessage('hello',$event)">点击 alert</a>
<!--
事件修饰符 v-on:event.stop.prevent.capture.self
详情查阅 Vue api
-->
<a href="http://www.baidu.com" @click.stop.prevent="editMessage">更改内容</a><br/>
</div>
<!-- CLASS 切换 -->
<div class="toggle">
<h4>v-show与v-if 以及class切换</h4>
<style>
.toggle div.show{height:100px;}
.toggle div.hide{height:0;}
</style>
<!-- checkbox 绑定 -->
<input type="checkbox" v-model="ok">按钮切换
<template v-if="ok">
<button @click="toggle">切换</button>
</template>
<template v-else>
<button @click="colorToggle">颜色切换</button>
</template>
<article>
<section>
v-show 与 v-if 还是有区别的;
当 OK为 false 时,
</section>
<section v-show="ok">
v-show 仅仅是将其隐藏;如果频繁切换,可采用v-show.不会进行大量的DOM操作
</section>
<section v-if="ok">
而 v-if 是直接移除该 dom,如果操作很少, v-if 可更好
</section>
</article>
<div :class="{'show':show,'hide':hide}" style="transition:0.4s;" :style="style">
</div>
</div>
<div class="input">
<h4>表单绑定</h4>
<!-- debounce 设置延迟 适合非快速更新视图的情况 -->
<input v-model="message" debounce="500"/>
<input type="checkbox" v-model="checked" /><br/>
<input id="LiLei" type="checkbox" v-model="names" value="LiLei"/>
<label for="LiLei">LiLei</label>
<input id="HanMei" type="checkbox" v-model="names" value="HanMei"/>
<label for="HanMei">HanMei</label>
<input id="Lucy" type="checkbox" v-model="names" value="Lucy"/>
<label for="Lucy">Lucy</label>
<input type="radio" value="one" v-model="radio"/>ONE
<input type="radio" value="two" v-model="radio"/>TWO
<select v-model="select" multiple>
<option selected>1</option>
<template v-for="n in 5">
<option>{{n + 2}}</option>
</template>
</select>
<div>
<p>
message:{{message}};
</p>
<p>
checkbox:{{checked}};
</p>
<p>
选中的名字有{{names}}
</p>
<p>
单选被选中的值为:{{radio}}
</p>
<p>
select 选中的值为:{{select}}
</p>
</div>
</div>
<div id="transition" style="min-height:300px;">
<h4>动画</h4>
<style>
.div1{background:#555;50px;height:50px;}
/* 该动画会一直存在 */
.expand-transition{transition:.4s ease;}
/* 该样式在进入的时候添加 */
.expand-enter{height:0;}
/* 该样式在退出的时候添加 */
.expand-leave{height:0;}
.div2{background:red;50px;height:50px;}
.flash-enter{animation:flash-in 1.4s;}
.flash-leave{animation:flash-out 1.4s;}
@keyframes flash-out{0%{opacity:1}30%{opacity:0;}40%{opacity:0.2;}100%{opacity:0;}}
@keyframes flash-in{0%{opacity:0}30%{opacity:1;}40%{opacity:0.2}100%{opacity:1;}}
</style>
<button @click="div1Toggle">DIV1 框架消失</button>
<!-- 指定动画名称 style 应当添加以该 动画名为首的样式 -->
<div v-if="show" class="div1" :transition="transitionName"></div>
<div v-if="show" class="div2" transition="flash"></div>
</div>
</body>
<script src="./vue.js"></script>
<script>
'use strict';
Vue.config.debug = true;
var vue1 = new Vue({
// 通过ID访问元素
// vue1.$el === document.getElementById('app') => true
el: '#app',
data: {
// 每个 Vue 实例都会代理其 data 对象里的所有属性
// 因此可通过 vue1.message 访问该属性
message: 'Hello Vue.js!!!'
},
// 生命周期勾子 created compiled ready destroyed 等 详情查阅 Vue api
created: function() {
//alert('我被创建了');
},
methods: {
show: function() {
alert(this.message);
}
}
});
// 监视该 实例下的 message
vue1.$watch('message', function(newVal, oldVal) {
// 改变后的值
console.log(newVal);
// 改变前的值
console.log(oldVal);
});
var vue2 = new Vue({
// 通过类名访问元素
el: '.list',
data: {
query:"",
items: [{
id: 1,
title: '标题1',
content: '内容1',
show: true,
}, {
id: 2,
title: '标题2',
content: '内容2',
show: true,
}, {
id: 3,
title: '标题3',
content: '内容3',
show: false
}]
},
});
/*
可对 vue2.items 直接进行数组操作,操作后可触发页面上内容的刷新
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
数组替换
==========================
以下两种都不会被 vue检测到数组变化
1. 直接用索引设置元素,如 vm.items[0] = {};
2. 修改数据的长度,如 vm.items.length = 0。
可采用 vue2.items.$set(0,{...}) 或 vue2.items.$remove(vue2.items[index])
去更新数组
*/
var vue3 = new Vue({
el: '.show',
data: {
first_name: 'Li',
last_name: 'Lei'
},
// TODO
// 当 first_name 与 last_name 有更改时,都会重新计算
computed: {
name: function(event) {
// 方法内的 this 指向 vue3
// event 是原生 DOM 事件
return this.first_name + this.last_name;
},
fullname: {
// getter
get: function() {
return this.first_name + this.last_name;
},
// setter
// vue3.fullname = 'Han Mei' 即可调用该 setter
set: function(newValue) {
var names = newValue.split(' ');
this.first_name = names[0];
this.last_name = name[1];
}
}
},
methods: {
showMessage: function(msg, event) {
console.log(msg);
// 阻止默认事件
event.preventDefault()
console.log(event);
console.log(this.name);
console.log(vue2.items[0].title);
},
editMessage: function() {
var content = prompt('请输入姓');
this.first_name = content;
// 更改 vue1 里的内容
vue1.message = content;
}
}
});
var vue4 = new Vue({
el: ".toggle",
data: {
hide: false,
style: {
background: '#555',
'100px'
},
styleStatus: true,
ok: false
},
methods: {
toggle: function() {
console.log(this.hide)
if (this.hide) {
this.hide = false;
} else {
this.hide = true;
}
},
colorToggle: function() {
if (this.styleStatus) {
this.styleStatus = false;
this.style = {
background: 'red',
'200px'
}
} else {
this.styleStatus = true;
this.style = {
background: '#555',
'100px'
};
}
},
},
computed: {
show: function() {
return this.hide ? false : true;
}
}
});
/*
前端发展到现在,一直遵循着 html css 以及 js 分离;
为了以后维护的时候能更方便;
然而 Vue 却在 html 里 却将 事件 通过 v-on 严格的绑定在 视图(HTML)上
我觉的除了官方的解释(模版与JS方法紧耦合,容易查找对应的JS;无需自己再手动 绑定事件与清理dom),
对使用者也是更加容易了;
可以将公用的 js 函数 提取出来,
然后通过不同的 Vue 实例去调用函数.以后有优化JS 时直接去优化该函数,
而且 视图 和 js 的紧耦合,可以使之更倾向于 组件化
*/
var vue5 = new Vue({
el: '.input',
data: {
message: '',
checked: '',
names: [],
radio: '',
select: ''
}
});
var vue6 = new Vue({
el: '#transition',
data: {
show: true,
// 指定动画名称
// 若该值为空, 默认采用 v-transition,v-enter,v-leave
transitionName: 'expand'
},
methods: {
div1Toggle: function() {
if (this.show) {
this.show = false;
} else {
this.show = true;
}
}
}
});
Vue.transition('expand', {
beforeEnter: function(el) {
el.textContent = 'beforeEnter'
},
// enter 及 leave 都可添加回调函数
enter: function(el,done) {
el.textContent = 'enter'
// enter 后5秒再执行 afterEnter
setTimeout(done,5000);
},
afterEnter: function(el) {
el.textContent = 'afterEnter'
},
enterCancelled: function(el) {
// handle cancellation
},
beforeLeave: function(el) {
el.textContent = 'beforeLeave'
},
leave: function(el) {
el.textContent = 'leave'
},
afterLeave: function(el) {
el.textContent = 'afterLeave'
},
leaveCancelled: function(el) {
// handle cancellation
}
});
</script>
</html>
- 了解完基础用法,开始做个 todo 页面
<!doctype html>
<html>
<head>
<title> 简单 TODO </title>
<style>
body{background:#d9d9d9;}
#todo{800px;margin:10px auto;background:#fff;box-shadow:0 0 3px rgba(0 0 0 255);transition:.4s ease;padding:16px;}
#todo>input{100%;padding:8px 0;height:32px;outline:none;font-size:18px;border:0;border-bottom:1px solid #d9d9d9;overflow:hidden;}
.bar{padding:16px 0;}
#todo>a{18%;display:inline-block;}
h1{text-align:center;}
ul{list-style:none;100%;padding:0;}
ul>li{height:48px;line-height:48px;overflow:hidden;border-bottom:1px solid #999;}
li>a{transform:rotate(45deg);font-weight:bold;color:red;float:right;font-size:32px;display:none;}
li>span{margin-left:32px;transition:.4s;}
li>input{position:relative;outline:none;}
li>input:before{content:'';18px;height:18px;background:#fff;border:1px solid #d9d9d9;top:-2px;position:absolute;z-index:1;}
li.checked>input:after{content:'';12px;height:6px;border-left:3px solid #999;border-bottom:3px solid #999;top:2px;left:2px;position:absolute;z-index:2;transform:rotate(-45deg);}
li.checked>span{text-decoration:line-through;color:#555;}
li:hover>a{display:inline-block;cursor:pointer;}
.fade-transition{transition:.4s ease}
.fade-enter,.fade-leave{height:0;}
.bar>span{color:#999;}
.bar>span:nth-child(n+2){margin-left:32px;padding:4px 8px;}
.bar>span:nth-child(n+2):hover{color:#555;transition:.4s;cursor:pointer;}
.bar>span.active{box-shadow:0 0 4px #222;}
</style>
</head>
<body>
<h1>待办事项</h1>
<div id="todo">
<input v-model="todo" type="text" v-on:keyup.enter="add" placeholder="请输入待办事项"/>
<div class="bar">
<span>共有 {{items.length}} 条代办事项</span>
<span @click='all' :class="{active:filter.all}">全部事项</span>
<span @click='undone' :class="{active:filter.undone}">未完成</span>
<span @click='done' :class="{active:filter.done}">已完成事项</span>
</div>
<ul>
<template v-for="item in items | orderBy 'todoid' | filterBy filter.status ">
<li transition="fade" :class="{'checked':item.status}">
<input type="hidden" v-model='item.todoid'>
<input type="checkbox" @click='status($index)' v-model='item.status'>
<span>{{item.content}}</span>
<!-- 获取INDEX -->
<a v-on:click="remove($index)">+</a>
</li>
</template>
</ul>
</div>
</body>
<script src="./vue.js"></script>
<script>
var todoList = new Vue({
el: '#todo',
data: {
test:false,
filter:{
all:true,
done:false,
undone:false,
status:'',
},
todoid:'1',
items: [],
todo:''
},
methods: {
remove: function(index) {
this.items.splice(index, 1);
},
add: function() {
if (this.todo.trim()) {
this.items.push({
content: this.todo,
todoid:this.todoid,
status:false
});
this.todoid ++ ;
localStorage.todoId = JSON.stringify(this.todoid);
}
this.todo = "";
},
status:function(index){
if(this.items[index].status){
this.items[index].status = false;
}else{
this.items[index].status = true;
}
},
all:function(){
this.filter = {
all:true,
done:false,
undone:false,
status:'',
}
},
undone:function(){
this.filter = {
all:false,
done:false,
undone:true,
status:false,
}
},
done:function(){
this.filter = {
all:false,
done:true,
undone:false,
status:true,
}
}
},
});
todoList.$watch('items',function(newValue,oldValue){
localStorage.todo = JSON.stringify(newValue);
// 内部值变化时,也能获取到变化
},{deep:true});
if(localStorage.todo){
todoList.items = JSON.parse(localStorage.todo);
todoList.todoid = JSON.parse(localStorage.todoId);
}
</script>
</html>