Vue (读音 /vju?/,类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。
作者:尤雨溪
特点:
Vue的核心是一个允许采用简洁的模板语法来声明式地将数据渲染进 DOM 的系统
<body>
<div id="app" >
<h1>Hellp,{{name}}</h1>
</div>
</body>
<script src="../js/vue2.7.15.js"></script>
<script>
const vm = new Vue({
el : '#app', // 绑定容器
data:{
name :'张三'
}
});
</script>
总结:
<body>
<div id="app">
<!--插值表达式-->
<h1>你好,{{name}}</h1>
<hr/>
<!--指令语法 v-test 文本插入 -->
<h1 v-text="'Hello,'+name"></h1>
<!-- v-bind:xxx 属性定义 -->
<a v-bind:href = "url">点击跳转百度</a>
<!--简写v-bind:xxx :xxx -->
<input :value="name" ></input>
</div>
</body>
<script src="../js/vue2.7.15.js"></script>
<script>
const vm = new Vue({
el:'#app',
data:{
name:'张三',
url:'https://www.baidu.com'
}
});
</script>
插值表达式
用于解析标签的内容(文本)
{{xxx}} xxx js的表达式,可以读取到data中的所有属性
指令语法
用于解析标签(标签的属性,标签的文本内容,标签的事件…)
Vue的指令多数以v-开头
<body>
<div id="root">
<!--单向数据绑定-->
<input type="text" :value="name" ><br/>
<!--<input type="text" v-bind:value="name">-->
<!--双向数据绑定-->
<input type="text" v-model="name">
<!-- 双向数据绑定 -->
<!--<input type="text" v-model:value="name">-->
</div>
</body>
<script src="../js/vue2.7.15.js"></script>
<script>
let vm = new Vue({
el: "#root",
data:{
name:'张三'
}
});
</script>
Vue中有两种数据绑定方式:
单向数据绑定
v-bind:xxx 数据只能从data到页面
双向数据绑定
v-model:value 双向数据绑定不仅能从data到页面,也能从页面到data
v-model一般用于表单元素,比如input,select …
<body>
<div id="root">
<h1>{{name}}</h1>
</div>
</body>
<script src="../js/vue2.7.15.js"></script>
<script>
const vm = new Vue({
// 第一种写法
el:"#root",
data(){
return {
name:'张三'
}
}
});
// 第二种el的写法
vm.$mount('#root');
</script>
el与data的两种写法
data的写法:
<style>
#myInput {
width: 400px;
height: 50px;
font-size: 40px;
color: red;
}
#contain {
overflow: auto;
margin-top: 20px;
width: 400px;
height: 200px;
border: 1px solid salmon;
}
</style>
<body>
<input id="myInput" type="text"/>
<div id="contain">
</div>
</body>
<script type="text/javascript">
let oIn = document.getElementById("myInput");
let oDiv = document.getElementById("contain");
let text;
window.data = {};
oIn.addEventListener('input', function (e) {
//获取当前input的value属性
text = e.target.value;
window.data.value = text;
});
Object.defineProperty(window.data,'value',{
get(){
return this.value;
},
set(v){
oDiv.innerHTML = v;
}
})
</script>
Vue中的数据通过vm对象来代理data中的所有属性的读写
Vue中的数据代理的好处:更方便操作data中的数据
原理:
绑定事件写法1:v-on:xxx
绑定事件写法2:@xxx
<style>
* {
margin-top: 20px;
}
.demo{
height: 50px;
background-color: pink;
}
.box1 {
padding: 5px;
background-color: green;
}
.box2 {
padding: 5px;
background-color: orange;
}
.list {
width: 200px;
height: 200px;
background-color: peachpuff;
overflow: auto;
}
li {
height: 100px;
}
</style>
<body>
<div id="root">
<!--绑定事件写法1:v-on:xxx -->
<!--绑定事件写法2:@xxx -->
<a v-on:click="showInfo" :href="url">点击跳转百度</a>
<!--@click.prevent 阻止默认行为-->
<!--绑定点击事件 @click-->
<a @click.prevent="showInfo" :href="url">点击跳转百度</a>
<!--$evnet 事件占位符-->
<button @click="showName($event,'事件占位符测试')">事件占位符测试</button>
<!--@click.stop 阻止冒泡-->
<div class="demo1" @click="showInfo">
<button @click.stop="showInfo">点击测试冒泡</button>
</div>
<!--@click.once 不管点击多少次,只执行一次-->
<button @click.once="showInfo">点击执行一次</button>
<!--@click.capture 用于捕获模式下的事件处理。当一个事件被触发时,它会从内部元素向外部元素传播,直到到达根元素。-->
<!--下面案例先触发A,再触发B-->
<div class="box1" @click.capture="showMsg('A')">
div1
<div class="box2" @click="showMsg('B')">
div2
</div>
</div>
<!--wheel.passive 事件的默认行为立即执行,无序等待事件的回调-->
<ul class="list" @wheel.passive="demo">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
</div>
</body>
<script src="../js/vue2.7.15.js"></script>
<script>
const vm = new Vue({
data(){
return {
url:'https://www.baidu.com'
}
},
methods:{
showInfo(){
console.log('这是一个函数')
},
showName(event,msg){
console.log(msg);
},
showMsg(msg){
console.log(msg)
},
demo(){
for(let i = 0;i<1000;i++){
console.log("*.*")
}
console.log("执行结束")
}
}
});
vm.$mount("#root");
</script>
<body>
<div id="root">
<input type="text" v-model="name" @keydown.enter="demo" >
</div>
</body>
<script src="../js/vue2.7.15.js"></script>
<script>
const vm = new Vue({
data(){
return {
name :'张三'
}
},
methods:{
demo(event) {
console.log(event.key)
console.log(event.keyCode)
}
}
});
vm.$mount("#root")
</script>
@keydowm.xxx
使用场景:通过已有属性生成一个新的属性
<body>
<div id="root">
姓:<input type="text" placeholder="请输入姓" v-model="fName" ><br/>
名:<input type="text" placeholder="请输入名" v-model="lName">
<hr/>
<h3>姓名:{{fullName}}</h3>
<!--存在缓存,可以二次复用-->
<h3>姓名:{{fullName}}</h3>
<h3>姓名:{{fullName}}</h3>
<hr/>
<input type="text" v-model="fullName">
</div>
</body>
<script src="../js/vue2.7.15.js"></script>
<script>
const vm = new Vue({
data(){
return {
fName:'',
lName:''
}
},
methods:{},
computed:{
// 简写计算属性 只读
// fullName(){
// return this.fName+this.lName;
// }
fullName:{
get(){
console.log("执行了Get")
return this.fName+'-'+this.lName;
},
set(value){
console.log(value);
let arr = value.split("-");
this.fName=arr[0];
this.lName=arr[1];
}
}
}
});
vm.$mount("#root");
</script>
计算属性:
<body>
<div id="root">
<h2>a的值:{{nums.a}}</h2>
<button @click="nums.a++">点击a++</button>
<h2>b的值:{{nums.b}}</h2>
<button @click="nums.b++">点击b++</button>
</div>
</body>
<script src="../js/vue2.7.15.js"></script>
<script>
const vm = new Vue({
data(){
return {
nums:{
a:1,
b:2
}
}
},
methods:{},
watch:{
nums:{
immediate:true,
deep:true,
handler(newValue,oldValue){
console.log(newValue,oldValue)
}
}
}
});
vm.$mount("#root")
</script>
当被监视的属性改变时,回调函数会自动调用。
监视的属性必须存在才能进行监视
监视属性两种写法:
vm.$watch('nums.a', {
immediate: true,
// deep:true, //深度监视
handler(newValue, oldValue) {
console.log(newValue, oldValue)
}
})
深度监视: deep:true
Vue 的watch默认不监测对象内部的值的改变,在watch中配置deep:true 可以检测对象内部的属性的值改变
<style>
.basic {
width: 400px;
height: 100px;
border: 1px solid black;
}
.demoA {
border: 4px solid red;
background-color: orange;
}
.demoB {
border: 4px dashed pink;
background-color: gray;
}
.demoC {
background-color: skyblue;
}
.box1 {
background-color: yellow;
}
.box2 {
font-size: 30px;
text-shadow: 2px 2px 10px red;
}
.box3 {
border-radius: 20px;
}
</style>
<body>
<div id="root">
<div class="basic" :class="mood" @click="changeMood">
{{name}}
</div>
<div class="basic" :class="classArr">
{{name}}
</div>
<div class="basic" :class="classObj">
{{name}}
</div>
<div class="basic" :style="styleObj">
{{name}}
</div>
<div class="basic" :style="styleArr">
{{name}}
</div>
</div>
<script src="../js/vue2.7.15.js"></script>
<script type="text/javascript">
const vm = new Vue({
data() {
return {
name: '张三',
mood: 'demoC',
classArr:[
'box1','box2'
],
classObj:{
box1:true,
box2:true,
box3:true
},
styleObj:{
fontSize:'40px',
color:'red'
},
styleArr:[
{
fontSize:'40px',
color:'blue'
},
{
backgroundColor: 'yellow'
}
]
}
},
methods: {
changeMood() {
const arr = ['demoA','demoB','demoC'];
const index = Math.floor(Math.random() * 3);
this.mood = arr[index];
}
}
});
vm.$mount("#root")
</script>
</body>
Class绑定:
语法 —> :class=’xxx‘ xxx可以是字符串 数组 对象
Style绑定:
语法:
指令:v-if 、v-if-else、v-else 、v-show
<body>
<div id="root">
<h1>当前的count值:{{count}}</h1>
<button @click="count++">Count++</button>
<hr/>
<div v-if="count==1">1</div>
<div v-else-if="count==2">2</div>
<div v-else>其他</div>
<div v-show="count==3"><h1>v-show</h1></div>
</div>
</body>
<script src="../js/vue2.7.15.js"></script>
<script>
const vm = new Vue({
data(){
return{
count:0
}
},
methods:{
}
});
vm.$mount("#root");
</script>
v-if和v-show的区别
v-if
是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。
v-if
也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块
相比之下,v-show
就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换
一般来说,v-if
有更高的切换开销,而 v-show
有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show
较好;如果在运行时条件很少改变,则使用 v-if
较好。
<body>
<div id="root">
<!--遍历数组-->
<ul>
<!-- (student,index) in students index:下标 -->
<li v-for="(student,index) in students" :key="student.stuNo">
{{index}}-{{student.stuNo}}-{{student.name}}
</li>
</ul>
<hr>
<!--遍历对象 (属性值,属性名)-->
<ul>
<li v-for="(value,key) in person" :key="key" >
{{key}} ---->> {{value}}
</li>
</ul>
<hr>
<!--遍历字符串 (字符,下标)-->
<ul>
<li v-for="(char,index) in str" :key="index">
{{index}} --->> {{char}}
</li>
</ul>
<hr>
<ul>
<li v-for="(num,index) in 5">
{{index}} --->> {{num}}
</li>
</ul>
</div>
</body>
<script src="../js/vue2.7.15.js"></script>
<script>
const vm = new Vue({
data(){
return {
students:[
{stuNo:"001",name:"张三"},
{stuNo:"002",name:"李四"},
{stuNo:"003",name:"王五"}
],
person:{
id:"101",
name:"谢广坤",
sex:"男"
},
str:'Hello,world'
}
},
methods:{}
});
vm.$mount("#root");
</script>
<body>
<div id="root">
<ul>
<!-- :key="stu.stuNo" -->
<li v-for="(stu,index) in students" :key="index" >
{{index}}-{{stu.name}}
<input type="text"/>
</li>
</ul>
<button @click="addStudent">添加学生</button>
</div>
<script src="./js/vue.js"></script>
<script type="text/javascript">
const vm = new Vue({
data() {
return {
students: [
{stuNo: 'S10101', name: '张三'},
{stuNo: 'S10102', name: '李四'},
{stuNo: 'S10103', name: '王五'}
]
}
},
methods: {
addStudent() {
let obj = {stuNo: 'S10104', name: '赵六'};
this.students.unshift(obj);
}
}
});
vm.$mount("#root")
</script>
</body>
:key='xxx'
:xxx最好是唯一属性值,上述案例使用index在特殊场景下会出现问题,例如:在students数组的头添加元素,页面上的input
元素会产生类似错位的现象;若使用stu.stuNo当作key时,比对时会把元素当作一个整体,不会出现类似错位的现象
<body>
<div id="root">
<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="(student,index) in filStudents" :key="student.stuNo">
{{index}} -{{student.stuNo}}-{{student.name}}-{{student.age}}
</li>
</ul>
</div>
<script src="./js/vue.js"></script>
<script type="text/javascript">
const vm = new Vue({
data() {
return {
sortType: 0,
keyWord: '',
students: [
{stuNo: 'S10101', name: '张三', age: 20},
{stuNo: 'S10102', name: '李四', age: 22},
{stuNo: 'S10103', name: '王五', age: 18},
{stuNo: 'S10102', name: '李五', age: 30}
],
}
},
computed: {
filStudents() {
const arr = this.students.filter(item => {
return item.name.indexOf(this.keyWord) != -1;
})
if (this.sortType) {
arr.sort((s1, s2) => {
return this.sortType == 1 ? s2.age - s1.age : s1.age - s2.age;
});
}
return arr;
}
}
});
vm.$mount("#root")
</script>
</body>
<body>
<div id="root">
<form action="#" method="post" @submit.prevent="demo">
账号: <input type="text" v-model="userInfo.account"><br/><br/>
密码: <input type="password" v-model="userInfo.password"><br/><br/>
年龄: <input type="number" v-model="userInfo.age"><br/><br/>
性别:
<label><input type="radio" name="sex" v-model="userInfo.sex" value="男"> 男 </label>
<label><input type="radio" name="sex" v-model="userInfo.sex" value="女"> 女 </label><br/><br/>
爱好:
<label><input type="checkbox" v-model="userInfo.hobby" value="1">唱</label>
<label><input type="checkbox" v-model="userInfo.hobby" value="2">跳</label>
<label><input type="checkbox" v-model="userInfo.hobby" value="3">Rap</label>
<label><input type="checkbox" v-model="userInfo.hobby" value="4">篮球</label>
<br><br>
城市:
<select v-model="userInfo.city">
<option value="">请选择</option>
<option value="beijing">北京</option>
<option value="shanghai">上海</option>
<option value="dandong">丹东</option>
</select>
<br><br>
其他:
<textarea cols="30" rows="10" v-model.lazy="userInfo.other">
</textarea><br><br>
<input type="checkbox" v-model="userInfo.agree"><a href="#">已阅读XXX</a><br><br>
<button type="submit">提交</button>
</form>
</div>
</body>
<script src="../js/vue2.7.15.js"></script>
<script>
const vm =new Vue({
el:"#root",
data(){
return {
userInfo:{
account:'',
password:'',
age:18,
sex:'男',
hobby:[],
city:'beijing',
other:'',
agree:''
}
}
},
methods:{
demo(){
console.log(this.userInfo);
return false;
}
}
});
</script>
案例:时间转换
导入Day.js —> Day.js地址:https://unpkg.com/dayjs@1.8.21/dayjs.min.js也可以下载采用离线模式
<body>
<div id="root">
<h2>时间</h2>
<h3>当前时间:{{time}}</h3>
<input v-bind:value="time | timeFormater()"/>
<h3>转换后的时间:{{time | timeFormater('YYYY-MM-DD HH:mm:ss')}}</h3>
<h3>转换后的时间:{{time | timeFormater('YYYY-MM-DD HH:mm:ss') | strSlice}}</h3>
</div>
</body>
<script src="../js/vue2.7.15.js"></script>
<script src="../js/dayjs.min.js"></script>
<script type="text/javascript">
Vue.filter('strSlice',function (value) {
return value.slice(0,11)
})
const vm = new Vue({
data(){
return {
time:1702366792000
}
},
filters:{
//局部过滤器
timeFormater(value,str='YYYY年MM月DD日 HH:mm:ss'){
return dayjs(value).format(str)
}
}
});
vm.$mount("#root")
</script>
v-bind:单向数据绑定
v-model:双向数据绑定
v-for:循环
v-if、v-if-else、v-else、v-show:条件渲染
v-text:文本替换
v-html:一般不使用,存在安全问题
v-cloak:本质是一个特殊的属性 Vue的实例创建完毕并接管容器后 会删除v-cloak
需要css的配置 解决网速慢时 页面展示{{}}(显示插值)的问题
v-once:所在的节点 初次被渲染后 就视为静态内容
v-pre:跳过所在的节点的编译过程
<style>
[v-cloak]{
display: none;
}
</style>
<body>
<div id="root">
<div v-text="name">
这是文本内容
</div>
<div v-html="str">
</div>
<div v-cloak>
{{name}}
</div>
<div v-once>
{{name}}
</div>
<div v-pre>
{{name}}
</div>
</div>
</body>
<script src="../js/vue2.7.15.js"></script>
<script>
const vm = new Vue({
data(){
return{
name:'张三',
str: '<b>张三</b>'
}
},
methods:{
}
});
vm.$mount("#root")
</script>
除了核心功能默认内置的指令 (v-model
和 v-show
),Vue 也允许注册自定义指令。注意,在 Vue2.0 中,代码复用和抽象的主要形式是组件。然而,有的情况下,你仍然需要对普通 DOM 元素进行底层操作,这时候就会用到自定义指令。
<body>
<div id="root">
<h2>当前n的值是:<span v-text="n"></span></h2>
<h3>方法n的值(2倍):<span v-big="n"></span></h3>
<button @click="n++">n的自增</button>
<input type="text" v-fbind="n" />
</div>
</body>
<script src="../js/vue2.7.15.js"></script>
<script>
const vm = new Vue({
data(){
return {
n:1
}
},
directives:{
// 自定义指令
big(element,binding){
console.log('big',this)
console.log(element,binding)
// 获取指令中的参数
element.innerText = binding.value*2;
},
fbind:{
//指令与元素成功绑定时执行(一进来)
bind(element,binding){
console.log("bind");
element.value = binding.value;
},
//指令所在的元素被载入页面时执行
inserted(element,binding){
console.log("inserted");
element.focus();
},
//指令所在的模板被解析时
update(element,binding){
console.log("update");
element.value = binding.value;
}
}
}
});
vm.$mount("#root");
</script>
上述fbind也可以通过Vue.directive
Vue.directive('fbind',{
//指令与元素成功绑定时执行(一进来)
bind(element,binding){
console.log("bind");
element.value = binding.value;
},
//指令所在的元素被载入页面时执行
inserted(element,binding){
console.log("inserted");
element.focus();
},
//指令所在的模板被解析时
update(element,binding){
console.log("update");
element.value = binding.value;
}
})
Vue生命周期也叫生命周期钩子
<body>
<div id="root">
<h2>n的值为:{{n}}</h2>
<button @click="n++">n++</button>
<button @click="bye">结束Vue生命周期</button>
<hr>
<h2 :style="{opacity}" >欢迎光临,{{name}}</h2>
</div>
</body>
<script src="../js/vue2.7.15.js"></script>
<script>
const vm = new Vue({
data() {
return {
name:"张三",
opacity:1,
n: 1
}
},
beforeCreate(){
console.log("beforeCreate");
},
created(){
console.log("created")
},
beforeMount(){
console.log("beforeMount")
},
mounted(){
console.log("mounted");
// 透明度由100%-0,循环
this.timer = setInterval(()=>{
this.opacity -= 0.01;
if (this.opacity <=0){
this.opacity = 1;
}
},20)
},
beforeUpdate(){
console.log("beforeUpdate")
},
updated(){
console.log("updated")
},
beforeDestroy(){
console.log("beforeDestroy")·
// 清除定时器
clearInterval(this.timer);
},
destroyed(){
console.log("destroyed")
},
methods:{
bye(){
//结束vue声明周期
this.$destroy();
}
}
});
vm.$mount("#root");
</script>
组件:是用来实现局部功能的代码和资源的集合
Vue中使用组件的三个步骤:
Vue组件为什么没有el?
最终所有的组件都要经过vm的管理,由vm中的el来决定在那个容器中展示
组件名:
-
连接,eg:my-schoolVueComponent
School组件本质是一个VueComponent 的构造函数并不是我们创建的 是Vue.extend生成的
<school></school>
在使用时Vue解析时会帮我们创建,school组件的实例对象,new VueComponent({…}),每次调用Vue.extend 都会返回一个全新的 VueComponent
VueComponent.prototype.__proto__ === Vue.prototype
:VueComponent.prototype.__proto__
:指向Vue
Vue.prototype
:指向Vue
this指向问题:
组件的this指向组件的本身,如school中的this指向school,student的this指向student,而Vue实例的this指向vm
<body>
<div id="root">
<school></school>
</div>
</body>
<script src="../js/vue2.7.15.js"></script>
<script>
const student = Vue.extend({
template:`
<div>
<h2>学生姓名:{{studentName}}</h2>
<h2>学生年龄:{{studentAge}}</h2>
</div>
`,
data(){
return{
studentName:'张三',
studentAge:18
}
}
});
// 创建一个vue组件
const school = Vue.extend({
template:`
<div>
<h2>学校名称:{{schoolName}}</h2>
<h2>学校地址:{{address}}</h2>
<hr/>
<student></student>
</div>
`,
data(){
return{
schoolName:'清华大学',
address:'北京'
}
},
components:{
student // 组件的嵌套
}
});
const vm = new Vue({
data(){
return {
}
},
components:{
school
}
});
vm.$mount("#root");
</script>