概要

vue的官方文档

一.vue的核心

1.初始vue

创建一个vue的实例
const x = new vue({});
下面的代码快速了解vue,快速知道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><!-- 引入vue --><script  src="../js/vue.js"></script>
</head>
<body>    <!-- 准备好一个容器 --><div id="app"><h1>hello,{{message}}</h1>  双括号里可以写javascript的表达式 如a,a+b,demo(a),x===y?'a':'b'等不是代码:如if(){}   for(){}</div><script>Vue.config.productionTip = false //以阻止 vue 在启动时生成生产提示// 创建一个实例 new Vue({el:'#app',     //el 用于指定当前Vue位于哪个容器中,值通常为为css选择器==字符串==data:{          //data中用于存放数据,数据供el所指定的容器去使用,值占时先写成一个对象。message:'你好',}})</script></body>
</html>

2.模板语法


v-bind之后就当成表达式了
可以简写成:
总结

3.数据绑定

<!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><!-- 引入vue --><script  src="../js/vue.js"></script>
</head>
<body><!-- 准备好一个容器 --><div id="root"><!-- 普通写法 -->单向数据的绑定: <input type="text" v-bind:value="name"><br>双向数据的绑定: <input type="text" v-model:value="name"><br><!-- 简单写法 -->单向数据的绑定: <input type="text" :value="name"><br>双向数据的绑定: <input type="text" v-model="name"><br><!-- 如下代码是错误的,因为v-mode只能应用在表单类元素中(输入类元素)上 --><h1 v-mode:x="name">你好啊</h1></div><script>Vue.config.productionTip = false //以阻止 vue 在启动时生成生产提示new Vue({el:"#root",data:{name:"为将去哪"}})</script>
</body>
</html>


总结:

4.el和data的两种写法


<!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><!-- 引入vue --><script  src="../js/vue.js"></script>
</head>
<body><!-- 准备好一个容器 --><div id="root"><h1>你好,{{name}}</h1></div><script>Vue.config.productionTip = false //以阻止 vue 在启动时生成生产提示// el的两种写法/* const v = new Vue({// el:"#root",   第一种写法data:{name:"鲸落"}})console.log(v);setTimeout(()=>{v.$mount('#root')  //第二种写法对于有定时器的时候更加的好},1000) *///data的两种写法const x = new Vue({// 第一种在上面// data的函数式写法(第二种写法)el:"#root",data:()=>{console.log("此处的this",this);return{name:'鲸落'}}})</script>
</body>
</html>

总结

5.MVVM模型



下面的只是为了验证,并没有什么具体的意义

比如随便写一个



总结

6.数据代理

6.1 回顾Object.defineProperty方法

这个方法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><script>let person = {name:"金落",sex:'男',age:12}console.log(person);        </script>
</body>
</html>


使用这个方法添加属性

 let person = {name:"金落",sex:'男',// age:12}Object.defineProperty(person,'age',{value:12})console.log(person);


这个时候的通过这个方法添加的属性age是不参加遍历的(也就是不参加枚举)

 let person = {name:"金落",sex:'男',// age:12}
Object.defineProperty(person,'age',{value:12
})
/* Object.keys()这个方法可以把传入其中的
对象的属性名提取出来组成一个数组 */
console.log(Object.keys(person));


进一步分析

  let person = {name:"金落",sex:'男',}Object.defineProperty(person,'age',{value:12,enumerable:true   //控制属性是否可以枚举})/* Object.keys()这个方法可以把参入其中的对象的属性名提取出来组成一个数组 */console.log(Object.keys(person));

let number = 22;let person = {name:"金落",sex:'男',}Object.defineProperty(person,'age',{/* 当有人读取person的age属性时,get函数(getter)就会被调用,且返回值就是age的值 */get:function(){return number}})/* Object.keys()这个方法可以把参入其中的对象的属性名提取出来组成一个数组 */console.log(person);


完整代码

<!DOCTYPE html>
<html><head><meta charset="UTF-8" /><title>回顾Object.defineproperty方法</title></head><body><script type="text/javascript" >let number = 18let person = {name:'张三',sex:'男',}Object.defineProperty(person,'age',{// value:18,// enumerable:true, //控制属性是否可以枚举,默认值是false// writable:true, //控制属性是否可以被修改,默认值是false// configurable:true //控制属性是否可以被删除,默认值是false//当有人读取person的age属性时,get函数(getter)就会被调用,且返回值就是age的值// get:function()可以简写成get()get(){console.log('有人读取age属性了')return number},//当有人修改person的age属性时,set函数(setter)就会被调用,且会收到修改的具体值set(value){console.log('有人修改了age属性,且值是',value)number = value}})// console.log(Object.keys(person))console.log(person)</script></body>
</html>
不喜欢就是不喜欢不在乎就是不在乎不明白就是不明白不在乎就是·

6.2 理解数据代理

简单的数据代理的例子

<!DOCTYPE html>
<html><head><meta charset="UTF-8" /><title>何为数据代理</title></head><body><!-- 数据代理:通过一个对象代理对另一个对象中属性的操作(读/写)--><script type="text/javascript" >let obj = {x:100}let obj2 = {y:200}Object.defineProperty(obj2,'x',{get(){return obj.x},set(value){obj.x = value}})</script></body>
</html>

这个就相当于数据的双向绑定了

6.3 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><!-- 引入vue --><script  src="../js/vue.js"></script>
</head>
<body><!-- 准备好一个容器 --><div id="root"><h1>学校名称:{{name}}</h1><h2>学校地址:{{address}}</h2></div><script>Vue.config.productionTip = false //以阻止 vue 在启动时生成生产提示const vm =  new Vue({el:"#root",data:{name:"鲸落",address:"武汉"}})</script>
</body>
</html>

7.事件处理

7.1 事件的基本使用

<!DOCTYPE html>
<html><head><meta charset="UTF-8" /><title>事件的基本使用</title><!-- 引入Vue --><script type="text/javascript" src="../js/vue.js"></script></head><body><!-- 事件的基本使用:1.使用v-on:xxx 或 @xxx 绑定事件,其中xxx是事件名;2.事件的回调需要配置在methods对象中,最终会在vm上;3.methods中配置的函数,不要用箭头函数!否则this就不是vm了;4.methods中配置的函数,都是被Vue所管理的函数,this的指向是vm 或 组件实例对象;5.@click="demo" 和 @click="demo($event)" 效果一致,但后者可以传参;--><!-- 准备好一个容器--><div id="root"><h2>欢迎来到{{name}}学习</h2><!-- <button v-on:click="showInfo">点我提示信息</button> --><button @click="showInfo1">点我提示信息1(不传参)</button><button @click="showInfo2($event,66)">点我提示信息2(传参)</button></div></body><script type="text/javascript">Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。const vm = new Vue({el:'#root',data:{name:'尚硅谷',},methods:{showInfo1(event){// console.log(event.target.innerText)// console.log(this) //此处的this是vmalert('同学你好!')},showInfo2(event,number){console.log(event,number)// console.log(event.target.innerText)// console.log(this) //此处的this是vmalert('同学你好!!')}}})</script>
</html>

7.2 事件的修饰符

  • prevent阻止事件的默认行为
<!DOCTYPE html>
<html><head><meta charset="UTF-8" /><title>事件修饰符</title><!-- 引入Vue --><script type="text/javascript" src="../js/vue.js"></script></head><body><div id="root"><h2>欢迎来到{{name}}学习</h2><!-- 1.阻止默认事件(常用) --><!-- 在这里a标签href写了一个网站,使用prevent之后,就会阻止它跳转。 --><a href="http://www.atguigu.com" @click.prevent="showInfo">点我提示信息</a></div></body><script type="text/javascript">Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。new Vue({el:'#root',data:{name:'尚硅谷'},methods:{showInfo(e){alert('同学你好!')// console.log(e.target)},}})</script>
</html>

  • stop阻止事件冒泡
<!DOCTYPE html>
<html><head><meta charset="UTF-8" /><title>事件修饰符</title><!-- 引入Vue --><style type="text/css">.demo1{height: 50px;background-color: skyblue;}</style><script type="text/javascript" src="../js/vue.js"></script></head><body><div id="root"><!-- 2.阻止事件冒泡(常用) --><div class="demo1" @click="showInfo"><button @click.stop="showInfo">点我提示信息</button><!-- 没有写stop的话,就会弹出两次,写了就阻止它到父元素的身上 --></div>    </div></body><script type="text/javascript">Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。new Vue({el:'#root',data:{name:'尚硅谷'},methods:{showInfo(e){alert('同学你好!')// console.log(e.target)},}})</script>
</html>

提示事件是先捕获再冒泡,而事件的触发是在事件冒泡阶段发生的

完整代码

<!DOCTYPE html>
<html><head><meta charset="UTF-8" /><title>事件修饰符</title><!-- 引入Vue --><script type="text/javascript" src="../js/vue.js"></script><style>*{margin-top: 20px;}.demo1{height: 50px;background-color: skyblue;}.box1{padding: 5px;background-color: skyblue;}.box2{padding: 5px;background-color: orange;}.list{width: 200px;height: 200px;background-color: peru;overflow: auto;}li{height: 100px;}</style></head><body><!-- Vue中的事件修饰符:1.prevent:阻止默认事件(常用);2.stop:阻止事件冒泡(常用);3.once:事件只触发一次(常用);4.capture:使用事件的捕获模式;5.self:只有event.target是当前操作的元素时才触发事件;6.passive:事件的默认行为立即执行,无需等待事件回调执行完毕;--><!-- 准备好一个容器--><div id="root"><h2>欢迎来到{{name}}学习</h2><!-- 1.阻止默认事件(常用) --><!-- 在这里a标签href写了一个网站,使用prevent之后,就会阻止它跳转。 --><a href="http://www.atguigu.com" @click.prevent="showInfo">点我提示信息</a><!-- 2.阻止事件冒泡(常用) --><div class="demo1" @click="showInfo"><button @click.stop="showInfo">点我提示信息</button><!-- 没有写stop的话,就会弹出两次,写了就阻止它到父元素的身上 --><!-- 修饰符可以连续写 --><a href="http://www.atguigu.com" @click.stop.prevent="showInfo">点我提示信息</a></div><!--3.事件只触发一次(常用) --><!-- 之后在点就不会触发了,除非刷新页面 --><button @click.once="showInfo">点我提示信息</button><!-- 使用事件的捕获模式 --><div class="box1" @click.capture="showMsg(1)">div1<div class="box2" @click="showMsg(2)">div2</div></div><!-- 只有event.target是当前操作的元素时才触发事件; --><div class="demo1" @click.self="showInfo"><button @click="showInfo">点我提示信息</button></div><!-- 事件的默认行为立即执行,无需等待事件回调执行完毕; --><ul @wheel.passive="demo" class="list"><li>1</li><li>2</li><li>3</li><li>4</li></ul></div></body><script type="text/javascript">Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。new Vue({el:'#root',data:{name:'尚硅谷'},methods:{showInfo(e){alert('同学你好!')// console.log(e.target)},showMsg(msg){console.log(msg)},demo(){for (let i = 0; i < 100000; i++) {console.log('#')}console.log('累坏了')}}})</script>
</html>

7.3 键盘事件

<!DOCTYPE html>
<html><head><meta charset="UTF-8" /><title>键盘事件</title><!-- 引入Vue --><script type="text/javascript" src="../js/vue.js"></script></head><body><!-- 1.Vue中常用的按键别名:回车 => enter删除 => delete (捕获“删除”和“退格”键)退出 => esc空格 => space换行 => tab (特殊,必须配合keydown去使用)上 => up下 => down左 => left右 => right2.Vue未提供别名的按键,可以使用按键原始的key值去绑定,但注意要转为kebab-case(短横线命名)3.系统修饰键(用法特殊):ctrl、alt、shift、meta(1).配合keyup使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发。(2).配合keydown使用:正常触发事件。4.也可以使用keyCode去指定具体的按键(不推荐)5.Vue.config.keyCodes.自定义键名 = 键码,可以去定制按键别名--><!-- 准备好一个容器--><div id="root"><h2>欢迎来到{{name}}学习</h2><input type="text" placeholder="按下回车提示输入" @keydown.huiche="showInfo"></div></body><script type="text/javascript">Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。Vue.config.keyCodes.huiche = 13 //定义了一个别名按键new Vue({el:'#root',data:{name:'尚硅谷'},methods: {showInfo(e){// console.log(e.key,e.keyCode)console.log(e.target.value)}},})</script></html>

8.计算属性

下面是本节要实现的效果,通过不同的方式实现进行比较,最后发现设计计算属性的目的。

8.1 姓名案例_插值语法实现

<!DOCTYPE html>
<html><head><meta charset="UTF-8" /><title>姓名案例_插值语法实现</title><!-- 引入Vue --><script type="text/javascript" src="../js/vue.js"></script></head><body><!-- 准备好一个容器--><div id="root">姓:<input type="text" v-model="firstName"> <br/><br/>名:<input type="text" v-model="lastName"> <br/><br/>全名:<span>{{firstName}}-{{lastName}}</span></div></body><script type="text/javascript">Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。new Vue({el:'#root',data:{firstName:'张',lastName:'三'}})</script>
</html>

8.2 姓名案例_methods实现

<!DOCTYPE html>
<html><head><meta charset="UTF-8" /><title>姓名案例_methods实现</title><!-- 引入Vue --><script type="text/javascript" src="../js/vue.js"></script></head><body><!-- 准备好一个容器--><div id="root">姓:<input type="text" v-model="firstName"> <br/><br/>名:<input type="text" v-model="lastName"> <br/><br/>全名:<span>{{fullName()}}</span><!-- <span>{{fullName}}</span> 这样写的话打印的就是里面的内容,在绑定事件的时候可以不写括号,但是在这里必须写 --></div></body><script type="text/javascript">Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。new Vue({el:'#root',data:{firstName:'张',lastName:'三'},methods: {fullName(){console.log('@---fullName')return this.firstName + '-' + this.lastName}},})</script>
</html>

8.3 姓名案例_计算属性实现

<!DOCTYPE html>
<html><head><meta charset="UTF-8" /><title>姓名案例_计算属性实现</title><!-- 引入Vue --><script type="text/javascript" src="../js/vue.js"></script></head><body><!-- 计算属性:1.定义:要用的属性不存在,要通过已有属性计算得来。2.原理:底层借助了Objcet.defineproperty方法提供的getter和setter。3.get函数什么时候执行?(1).初次读取时会执行一次。(2).当依赖的数据发生改变时会被再次调用。4.优势:与methods实现相比,内部有缓存机制(复用),效率更高,调试方便。5.备注:1.计算属性最终会出现在vm上,直接读取使用即可。2.如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生改变。--><!-- 准备好一个容器--><div id="root">姓:<input type="text" v-model="firstName"> <br/><br/>名:<input type="text" v-model="lastName"> <br/><br/>测试:<input type="text" v-model="x"> <br/><br/>全名:<span>{{fullName}}</span> <br/><br/><!-- 全名:<span>{{fullName}}</span> <br/><br/>全名:<span>{{fullName}}</span> <br/><br/>全名:<span>{{fullName}}</span> --></div></body><script type="text/javascript">Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。const vm = new Vue({el:'#root',data:{firstName:'张',lastName:'三',x:'你好'},methods: {demo(){}},computed:{fullName:{//get有什么作用?当有人读取fullName时,get就会被调用,且返回值就作为fullName的值//get什么时候调用?1.初次读取fullName时。2.所依赖的数据发生变化时。get(){console.log('get被调用了')// console.log(this) //此处的this是vm 是vue帮我们调成这样的return this.firstName + '-' + this.lastName},//set什么时候调用? 当fullName被修改时。set(value){console.log('set',value)const arr = value.split('-')this.firstName = arr[0]this.lastName = arr[1]}}}})</script>
</html>

8.4 姓名案例_计算属性简写

<!DOCTYPE html>
<html><head><meta charset="UTF-8" /><title>姓名案例_计算属性实现</title><!-- 引入Vue --><script type="text/javascript" src="../js/vue.js"></script></head><body><!-- 准备好一个容器--><div id="root">姓:<input type="text" v-model="firstName"> <br/><br/>名:<input type="text" v-model="lastName"> <br/><br/>全名:<span>{{fullName}}</span> <br/><br/></div></body><script type="text/javascript">Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。const vm = new Vue({el:'#root',data:{firstName:'张',lastName:'三',},//当确定了只读取,不修改的话就可以写成下面的简写方式computed:{//完整写法/* fullName:{get(){console.log('get被调用了')return this.firstName + '-' + this.lastName},set(value){console.log('set',value)const arr = value.split('-')this.firstName = arr[0]this.lastName = arr[1]}} *///简写fullName(){console.log('get被调用了')return this.firstName + '-' + this.lastName}}})</script>
</html>

9.监听属性

9.1 天气案例

<!DOCTYPE html>
<html><head><meta charset="UTF-8" /><title>天气案例</title><!-- 引入Vue --><script type="text/javascript" src="../js/vue.js"></script></head><body><!-- 准备好一个容器--><div id="root"><h2>今天天气很{{info}}</h2><!-- 绑定事件的时候:@xxx="yyy" yyy可以写一些简单的语句 (写一些简单的不要写太复杂)--><!-- <button @click="isHot = !isHot">切换天气</button> --><button @click="changeWeather">切换天气</button></div></body><script type="text/javascript">Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。const vm = new Vue({el:'#root',data:{isHot:true,},computed:{info(){return this.isHot ? '炎热' : '凉爽'}},methods: {changeWeather(){this.isHot = !this.isHot}},})</script>
</html>

9.2 天气案例_监听属性

<!DOCTYPE html>
<html><head><meta charset="UTF-8" /><title>天气案例_监视属性</title><!-- 引入Vue --><script type="text/javascript" src="../js/vue.js"></script></head><body><!-- 监视属性watch:1.当被监视的属性变化时, 回调函数自动调用, 进行相关操作2.监视的属性必须存在,才能进行监视!!3.监视的两种写法:(1).new Vue时传入watch配置(2).通过vm.$watch监视--><!-- 准备好一个容器--><div id="root"><h2>今天天气很{{info}}</h2><button @click="changeWeather">切换天气</button></div></body><script type="text/javascript">Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。const vm = new Vue({el:'#root',data:{isHot:true,},computed:{info(){return this.isHot ? '炎热' : '凉爽'}},methods: {changeWeather(){this.isHot = !this.isHot}},/* watch:{isHot:{immediate:true, //初始化时让handler调用一下//handler什么时候调用?当isHot发生改变时。handler(newValue,oldValue){console.log('isHot被修改了',newValue,oldValue)}}} */})vm.$watch('isHot',{immediate:true, //初始化时让handler调用一下//handler什么时候调用?当isHot发生改变时。handler(newValue,oldValue){console.log('isHot被修改了',newValue,oldValue)}})</script>
</html>

9.3 天气案例_深度监视

<!DOCTYPE html>
<html><head><meta charset="UTF-8" /><title>天气案例_深度监视</title><!-- 引入Vue --><script type="text/javascript" src="../js/vue.js"></script></head><body><!-- 深度监视:(1).Vue中的watch默认不监测对象内部值的改变(一层)。(2).配置deep:true可以监测对象内部值改变(多层)。备注:(1).Vue自身可以监测对象内部值的改变,但Vue提供的watch默认不可以!(2).使用watch时根据数据的具体结构,决定是否采用深度监视。--><!-- 准备好一个容器--><div id="root"><h2>今天天气很{{info}}</h2><button @click="changeWeather">切换天气</button><hr/><h3>a的值是:{{numbers.a}}</h3><button @click="numbers.a++">点我让a+1</button><h3>b的值是:{{numbers.b}}</h3><button @click="numbers.b++">点我让b+1</button><button @click="numbers = {a:666,b:888}">彻底替换掉numbers</button>{{numbers.c.d.e}}</div></body><script type="text/javascript">Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。const vm = new Vue({el:'#root',data:{isHot:true,numbers:{a:1,b:1,c:{d:{e:100}}}},computed:{info(){return this.isHot ? '炎热' : '凉爽'}},methods: {changeWeather(){this.isHot = !this.isHot}},watch:{isHot:{// immediate:true, //初始化时让handler调用一下//handler什么时候调用?当isHot发生改变时。handler(newValue,oldValue){console.log('isHot被修改了',newValue,oldValue)}},//监视多级结构中某个属性的变化/* 'numbers.a':{handler(){console.log('a被改变了')}} *///监视多级结构中所有属性的变化numbers:{deep:true,handler(){console.log('numbers改变了')}}}})</script>
</html>

9.4天气案例_监听属性_简写

<!DOCTYPE html>
<html><head><meta charset="UTF-8" /><title>天气案例_监视属性_简写</title><!-- 引入Vue --><script type="text/javascript" src="../js/vue.js"></script></head><body><!-- 准备好一个容器--><div id="root"><h2>今天天气很{{info}}</h2><button @click="changeWeather">切换天气</button></div></body><script type="text/javascript">Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。const vm = new Vue({el:'#root',data:{isHot:true,},computed:{info(){return this.isHot ? '炎热' : '凉爽'}},methods: {changeWeather(){this.isHot = !this.isHot}},watch:{//正常写法/* isHot:{// immediate:true, //初始化时让handler调用一下// deep:true,//深度监视handler(newValue,oldValue){console.log('isHot被修改了',newValue,oldValue)}}, *///简写/* isHot(newValue,oldValue){console.log('isHot被修改了',newValue,oldValue,this)} */}})//正常写法/* vm.$watch('isHot',{immediate:true, //初始化时让handler调用一下deep:true,//深度监视handler(newValue,oldValue){console.log('isHot被修改了',newValue,oldValue)}}) *///简写/* vm.$watch('isHot',(newValue,oldValue)=>{console.log('isHot被修改了',newValue,oldValue,this)}) */</script>
</html>

9.5 姓名案例_watch实现

 <!DOCTYPE html>
<html><head><meta charset="UTF-8" /><title>姓名案例_watch实现</title><!-- 引入Vue --><script type="text/javascript" src="../js/vue.js"></script></head><body><!-- computed和watch之间的区别:1.computed能完成的功能,watch都可以完成。2.watch能完成的功能,computed不一定能完成,例如:watch可以进行异步操作。两个重要的小原则:1.所被Vue管理的函数,最好写成普通函数,这样this的指向才是vm 或 组件实例对象。2.所有不被Vue所管理的函数(定时器的回调函数、ajax的回调函数等、Promise的回调函数),最好写成箭头函数,这样this的指向才是vm 或 组件实例对象。--><!-- 准备好一个容器--><div id="root">姓:<input type="text" v-model="firstName"> <br/><br/>名:<input type="text" v-model="lastName"> <br/><br/>全名:<span>{{fullName}}</span> <br/><br/></div></body><script type="text/javascript">Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。const vm = new Vue({el:'#root',data:{firstName:'张',lastName:'三',fullName:'张-三'},watch:{firstName(val){setTimeout(()=>{console.log(this)this.fullName = val + '-' + this.lastName},1000);},lastName(val){this.fullName = this.firstName + '-' + val}}})</script>
</html>

10.绑定样式

<!DOCTYPE html>
<html><head><meta charset="UTF-8" /><title>绑定样式</title><style>.basic{width: 400px;height: 100px;border: 1px solid black;}.happy{border: 4px solid red;;background-color: rgba(255, 255, 0, 0.644);background: linear-gradient(30deg,yellow,pink,orange,yellow);}.sad{border: 4px dashed rgb(2, 197, 2);background-color: gray;}.normal{background-color: skyblue;}.atguigu1{background-color: yellowgreen;}.atguigu2{font-size: 30px;text-shadow:2px 2px 10px red;}.atguigu3{border-radius: 20px;}</style><script type="text/javascript" src="../js/vue.js"></script></head><body><!-- 绑定样式:1. class样式写法:class="xxx" xxx可以是字符串、对象、数组。字符串写法适用于:类名不确定,要动态获取。对象写法适用于:要绑定多个样式,个数不确定,名字也不确定。数组写法适用于:要绑定多个样式,个数确定,名字也确定,但不确定用不用。2. style样式:style="{fontSize: xxx}"其中xxx是动态值。:style="[a,b]"其中a、b是样式对象。--><!-- 准备好一个容器--><div id="root"><!-- 绑定class样式--字符串写法,适用于:样式的类名不确定,需要动态指定 --><div class="basic" :class="mood" @click="changeMood">{{name}}</div> <br/><br/><!-- 绑定class样式--数组写法,适用于:要绑定的样式个数不确定、名字也不确定 --><div class="basic" :class="classArr">{{name}}</div> <br/><br/><!-- 绑定class样式--对象写法,适用于:要绑定的样式个数确定、名字也确定,但要动态决定用不用 --><div class="basic" :class="classObj">{{name}}</div> <br/><br/><!-- 绑定style样式--对象写法 --><div class="basic" :style="styleObj">{{name}}</div> <br/><br/><!-- 绑定style样式--数组写法 --><div class="basic" :style="styleArr">{{name}}</div></div></body><script type="text/javascript">Vue.config.productionTip = falseconst vm = new Vue({el:'#root',data:{name:'尚硅谷',mood:'normal',classArr:['atguigu1','atguigu2','atguigu3'],classObj:{atguigu1:false,atguigu2:false,},styleObj:{fontSize: '40px',color:'red',},styleObj2:{backgroundColor:'orange'},styleArr:[{fontSize: '40px',color:'blue',},{backgroundColor:'gray'}]},methods: {changeMood(){const arr = ['happy','sad','normal']const index = Math.floor(Math.random()*3)this.mood = arr[index]}},})</script></html>

运行结果

11.条件渲染

<!DOCTYPE html>
<html><head><meta charset="UTF-8" /><title>条件渲染</title><script type="text/javascript" src="../js/vue.js"></script></head><body><!-- 条件渲染:1.v-if写法:(1).v-if="表达式" (2).v-else-if="表达式"(3).v-else="表达式"适用于:切换频率较低的场景。特点:不展示的DOM元素直接被移除。注意:v-if可以和:v-else-if、v-else一起使用,但要求结构不能被“打断”。2.v-show写法:v-show="表达式"适用于:切换频率较高的场景。特点:不展示的DOM元素未被移除,仅仅是使用样式隐藏掉3.备注:使用v-if的时,元素可能无法获取到,而使用v-show一定可以获取到。--><!-- 准备好一个容器--><div id="root"><h2>当前的n值是:{{n}}</h2><button @click="n++">点我n+1</button><!-- 使用v-show做条件渲染 --><!-- <h2 v-show="false">欢迎来到{{name}}</h2> --><!-- <h2 v-show="1 === 1">欢迎来到{{name}}</h2> --><!-- 使用v-if做条件渲染 --><!-- <h2 v-if="false">欢迎来到{{name}}</h2> --><!-- <h2 v-if="1 === 1">欢迎来到{{name}}</h2> --><!-- v-else和v-else-if --><!-- <div v-if="n === 1">Angular</div><div v-else-if="n === 2">React</div><div v-else-if="n === 3">Vue</div><div v-else>哈哈</div> --><!-- v-if与template的配合使用 --><template v-if="n === 1"><h2>你好</h2><h2>尚硅谷</h2><h2>北京</h2></template></div></body><script type="text/javascript">Vue.config.productionTip = falseconst vm = new Vue({el:'#root',data:{name:'尚硅谷',n:0}})</script>
</html>


12.列表渲染(重要)

我们本节要实现的主要有下面的这些

12.1 基本列表渲染(较简单)

v-for指令

  • 用于展示列表数据
  • 语法:v-for=“(item, index) in xxx” :key=“yyy”
  • 可遍历:数组、对象、字符串(用的很少)、指定次数(用的很少)
    举例的实例代码如下
<!DOCTYPE html>
<html><head><meta charset="UTF-8" /><title>基本列表</title><script type="text/javascript" src="../js/vue.js"></script></head><body><!-- 准备好一个容器--><div id="root"><!-- 遍历数组 --><h2>人员列表(遍历数组)</h2><ul><li v-for="(p,index) of persons" :key="index">{{p.name}}-{{p.age}}</li></ul><!-- 遍历对象 --><h2>汽车信息(遍历对象)</h2><ul><li v-for="(value,k) of car" :key="k">{{k}}-{{value}}</li></ul><!-- 遍历字符串 --><h2>测试遍历字符串(用得少)</h2><ul><li v-for="(char,index) of str" :key="index">{{char}}-{{index}}</li></ul><!-- 遍历指定次数 --><h2>测试遍历指定次数(用得少)</h2><ul><li v-for="(number,index) of 5" :key="index">{{index}}-{{number}}</li></ul></div><script type="text/javascript">Vue.config.productionTip = falsenew Vue({el:'#root',data:{persons:[{id:'001',name:'张三',age:18},{id:'002',name:'李四',age:19},{id:'003',name:'王五',age:20}],car:{name:'奥迪A8',price:'70万',color:'黑色'},str:'hello'}})</script>
</html>

运行结果

12.2 key的原理(重要)

代码如下


<!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><!-- 引入vue --><script  src="../js/vue.js"></script>
</head>
<body><!-- 准备好一个容器 --><div id="root"><ul><!-- 遍历数组 --><h2>人员列表</h2><button @click.once="add">添加一个老刘</button><li v-for="(p,index) of persons" :key="index">{{p.name}}-{{p.age}}<input type="text"></li></ul></div><script>Vue.config.productionTip = falsenew Vue({el:"#root",data:{persons:[{id:'001',name:'张三',age:12},{id:'002',name:'李四',age:12},{id:'003',name:'王五',age:12}                  ]},methods: {add(){const p = {id:'004',name:'老刘',age:40};this.persons.unshift(p);  //往前面添加}},})</script>
</body>
</html>

对上面的代码进行标记

运行结果

当在每一个输入框中输入内容的时候

这个时候点击按钮,会看到


接下来,改一下

下面显示正常,没有错位了

详细原理如下
使用index作为key

=使用id作为key

总结

12.3 列表的过滤

  1. 使用watch实现
<!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><!-- 引入vue --><script  src="../js/vue.js"></script>
</head>
<body><div id="root"><ul><h2>人员列表</h2><input type="text" placeholder="请输入名字" v-model="keyWord"><li v-for="(p,index) in filpersons" :key="index">{{p.name}}-{{p.age}}-{{p.sex}}</li></ul></div><script>Vue.config.productionTip = false //以阻止 vue 在启动时生成生产提示//方式一: 用watch实现new Vue({el:"#root",data:{keyWord:"", //刚开始的时候为空persons:[{id:'001',name:'马冬梅',age:12,sex:'女'},{id:'002',name:'周冬雨',age:22,sex:'女'},{id:'003',name:'周杰伦',age:34,sex:'男'},{id:'004',name:'温兆伦',age:21,sex:'男'}],filpersons:[]},watch:{keyWord:{immediate:true, //可以走来就执行一次handler(val){this.filpersons = this.persons.filter((p)=>{return p.name.indexOf(val) !== -1;})}}}})</script>
</body>
</html>

代码分析


知识回顾
filter过滤器

indexof数组的一个方法
没有的就返回-1,
注意:空格也是返回-1

  1. 使用计算属性实现

这个方法比上面有watch要好一点,简单一点

<!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><!-- 引入vue --><script  src="../js/vue.js"></script>
</head>
<body><div id="root"><ul><h2>人员列表</h2><input type="text" placeholder="请输入名字" v-model="keyWord"><li v-for="(p,index) in filpersons" :key="index">{{p.name}}-{{p.age}}-{{p.sex}}</li></ul></div><script>Vue.config.productionTip = false //以阻止 vue 在启动时生成生产提示//方式一: 用watch实现new Vue({el:"#root",data:{keyWord:"", //刚开始的时候为空persons:[{id:'001',name:'马冬梅',age:12,sex:'女'},{id:'002',name:'周冬雨',age:22,sex:'女'},{id:'003',name:'周杰伦',age:34,sex:'男'},{id:'004',name:'温兆伦',age:21,sex:'男'}],},computed:{   //刚开始的时候就会执行一次 数据改变的时候也会执行filpersons(){return this.persons.filter((p)=>{return p.name.indexOf(this.keyWord) !==-1;})}}})</script>
</body>
</html>

12.4 列表的排序

首先先回顾一下数组中的sort方法,sort会改变原来的数组
详细教程sort方法文档
举例子

 Vue.config.productionTip = false //以阻止 vue 在启动时生成生产提示// 如果数组是数字的话,就需要在方法里面的函数里面传入两个参数let  arr = [1,4,3,2];// 根据a-b还是b-a确定是降序还是升序 默认是降序arr.sort(function(a,b){return a-b;  //第一参数减去第二参数  升序}) console.log(arr);arr.sort((a,b)=>{return b-a;  //第二个参数减去第一个参数 降序})console.log(arr);

<!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><!-- 引入vue --><script  src="../js/vue.js"></script>
</head>
<body><!-- 准备好一个容器 --><div id="root"><ul><h2>人员列表</h2><input type="text" placeholder="请输入名字" v-model="keyWord"><button @click="sortType = 2">年龄升序</button><button @click="sortType = 1">年龄降序</button><button @click="sortType = 0">原顺序</button><li v-for="(p,index) in filpersons" :key="p.id">{{p.name}}-{{p.age}}-{{p.sex}}<input type="text"></li></ul></div><script>Vue.config.productionTip = false //以阻止 vue 在启动时生成生产提示new Vue({el:"#root",data:{keyWord:"", //刚开始的时候为空sortType:0,  //原顺序是0, 1是降序 2是升序persons:[{id:'001',name:'马冬梅',age:12,sex:'女'},{id:'002',name:'周冬雨',age:22,sex:'女'},{id:'003',name:'周杰伦',age:34,sex:'男'},{id:'004',name:'温兆伦',age:21,sex:'男'}],},computed:{filpersons(){const arr = this.persons.filter((p)=>{return p.name.indexOf(this.keyWord) !==-1;})//判断是否需要排序if(this.sortType){arr.sort((p1,p2)=>{    //这里面的p1,p2是对象return this.sortType ===1? p2.age-p1.age : p1.age-p2.age})}return arr}}})</script>
</body>
</html>

运行结果

12.5 更新时的一个问题

<!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>                             <!-- 引入vue --><script  src="../js/vue.js"></script>
</head>
<body><!-- 准备好一个容器 --><div id="root"><ul><h2>人员列表</h2><button @click="updataMei">更新马冬梅的信息</button><li v-for="(p,index) in persons" :key="p.id">{{p.name}}-{{p.age}}-{{p.sex}}</li></ul></div><script>Vue.config.productionTip = false //以阻止 vue 在启动时生成生产提示const vm = new Vue({el:"#root",data:{persons:[{id:'001',name:'马冬梅',age:12,sex:'女'},{id:'002',name:'周冬雨',age:22,sex:'女'},{id:'003',name:'周杰伦',age:34,sex:'男'},{id:'004',name:'温兆伦',age:21,sex:'男'}]},methods: {updataMei(){/* this.persons[0].name = '马老师',this.persons[0].age = 50,this.persons[0].sex = '男'  */  //上面的可以成功,但是太麻烦了this.persons[0] ={id:'001',name:'马老师',age:59,sex:'男'}}},})</script>
</body>
</html>



当先点开控制台,再点开vue的时候

12.6 Vue检测数据改变的原理_对象

<!DOCTYPE html>
<html><head><meta charset="UTF-8" /><title>Vue监测数据改变的原理</title><!-- 引入Vue --><script type="text/javascript" src="../js/vue.js"></script></head><body><!-- 准备好一个容器--><div id="root"><h2>学校名称:{{name}}</h2><h2>学校地址:{{address}}</h2></div></body><script type="text/javascript">Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。const vm = new Vue({el:'#root',data:{name:'尚硅谷',address:'北京',student:{name:'tom',age:{rAge:40,sAge:29,},friends:[{name:'jerry',age:35}]}}})</script>
</html>

12.7 模拟一个数据检测

<!DOCTYPE html>
<html><head><meta charset="UTF-8" /><title>Document</title></head><body><script type="text/javascript" >let data = {name:'尚硅谷',address:'北京',}//创建一个监视的实例对象,用于监视data中属性的变化const obs = new Observer(data)      console.log(obs)    //准备一个vm实例对象let vm = {}vm._data = data = obsfunction Observer(obj){//汇总对象中所有的属性形成一个数组const keys = Object.keys(obj)//遍历keys.forEach((k)=>{Object.defineProperty(this,k,{get(){return obj[k]},set(val){console.log(`${k}被改了,我要去解析模板,生成虚拟DOM.....我要开始忙了`)obj[k] = val}})})}</script></body>
</html>

12.8 Vue.set的使用(后面再回来看)

12.9 Vue检测数据改变的原理_数组

12.10 总结数据检测

13.收集表单数据(比较轻松)

<!DOCTYPE html>
<html><head><meta charset="UTF-8" /><title>收集表单数据</title><script type="text/javascript" src="../js/vue.js"></script></head><body><!-- 收集表单数据:若:<input type="text"/>,则v-model收集的是value值,用户输入的就是value值。若:<input type="radio"/>,则v-model收集的是value值,且要给标签配置value值。若:<input type="checkbox"/>1.没有配置input的value属性,那么收集的就是checked(勾选 or 未勾选,是布尔值)2.配置input的value属性:(1)v-model的初始值是非数组,那么收集的就是checked(勾选 or 未勾选,是布尔值)(2)v-model的初始值是数组,那么收集的的就是value组成的数组备注:v-model的三个修饰符:lazy:失去焦点再收集数据number:输入字符串转为有效的数字trim:输入首尾空格过滤--><!-- 准备好一个容器--><div id="root"><form @submit.prevent="demo">账号:<input type="text" v-model.trim="userInfo.account"> <br/><br/>密码:<input type="password" v-model="userInfo.password"> <br/><br/>年龄:<input type="number" v-model.number="userInfo.age"> <br/><br/>性别:男<input type="radio" name="sex" v-model="userInfo.sex" value="male">女<input type="radio" name="sex" v-model="userInfo.sex" value="female"> <br/><br/>爱好:学习<input type="checkbox" v-model="userInfo.hobby" value="study">打游戏<input type="checkbox" v-model="userInfo.hobby" value="game">吃饭<input type="checkbox" v-model="userInfo.hobby" value="eat"><br/><br/>所属校区<select v-model="userInfo.city"><option value="">请选择校区</option><option value="beijing">北京</option><option value="shanghai">上海</option><option value="shenzhen">深圳</option><option value="wuhan">武汉</option></select><br/><br/>其他信息:<textarea v-model.lazy="userInfo.other"></textarea> <br/><br/><input type="checkbox" v-model="userInfo.agree">阅读并接受<a href="http://www.atguigu.com">《用户协议》</a><button>提交</button></form></div></body><script type="text/javascript">Vue.config.productionTip = falsenew Vue({el:'#root',data:{userInfo:{account:'',password:'',age:18,sex:'female',hobby:[],city:'beijing',other:'',agree:''}},methods: {demo(){console.log(JSON.stringify(this.userInfo))}}})</script>
</html>

运行结果

14.过滤器(不是必须的,先跳过)

15 内置指令

常用的内置属性

15.1 v-text指令(不常用)

这个不常用

<!DOCTYPE html>
<html><head><meta charset="UTF-8" /><title>v-text指令</title><!-- 引入Vue --><script type="text/javascript" src="../js/vue.js"></script></head><body><!-- 我们学过的指令:v-bind : 单向绑定解析表达式, 可简写为 :xxxv-model   : 双向数据绑定v-for   : 遍历数组/对象/字符串v-on       : 绑定事件监听, 可简写为@v-if        : 条件渲染(动态控制节点是否存存在)v-else     : 条件渲染(动态控制节点是否存存在)v-show     : 条件渲染 (动态控制节点是否展示)v-text指令:1.作用:向其所在的节点中渲染文本内容。2.与插值语法的区别:v-text会替换掉节点中的内容,{{xx}}则不会。--><!-- 准备好一个容器--><div id="root"><div>你好,{{name}}</div><div v-text="name"></div><div v-text="str"></div></div></body><script type="text/javascript">Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。new Vue({el:'#root',data:{name:'尚硅谷',str:'<h3>你好啊!</h3>'}})</script>
</html>

15.2 v-html指令(里面有关cookie的没看)

视频大概26分钟,看了4分钟,到时候需要的时候再看

<!DOCTYPE html>
<html><head><meta charset="UTF-8" /><title>v-html指令</title><!-- 引入Vue --><script type="text/javascript" src="../js/vue.js"></script></head><body><!-- v-html指令:1.作用:向指定节点中渲染包含html结构的内容。2.与插值语法的区别:(1).v-html会替换掉节点中所有的内容,{{xx}}则不会。(2).v-html可以识别html结构。3.严重注意:v-html有安全性问题!!!!(1).在网站上动态渲染任意HTML是非常危险的,容易导致XSS攻击。(2).一定要在可信的内容上使用v-html,永不要用在用户提交的内容上!--><!-- 准备好一个容器--><div id="root"><div>你好,{{name}}</div><div v-html="str"></div><div v-html="str2"></div></div></body><script type="text/javascript">Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。new Vue({el:'#root',data:{name:'尚硅谷',str:'<h3>你好啊!</h3>',str2:'<a href=javascript:location.href="http://www.baidu.com?"+document.cookie>兄弟我找到你想要的资源了,快来!</a>',}})</script>
</html>

运行结果

15.3 v-cloak指令(目前用不到)

先跳过

<!DOCTYPE html>
<html><head><meta charset="UTF-8" /><title>v-cloak指令</title><style>[v-cloak]{display:none;}</style><!-- 引入Vue --></head><body><!-- v-cloak指令(没有值):1.本质是一个特殊属性,Vue实例创建完毕并接管容器后,会删掉v-cloak属性。2.使用css配合v-cloak可以解决网速慢时页面展示出{{xxx}}的问题。--><!-- 准备好一个容器--><div id="root"><h2 v-cloak>{{name}}</h2></div><script type="text/javascript" src="http://localhost:8080/resource/5s/vue.js"></script></body><script type="text/javascript">console.log(1)Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。new Vue({el:'#root',data:{name:'尚硅谷'}})</script>
</html>

15.4 v-once指令(这个简单)

<!DOCTYPE html>
<html><head><meta charset="UTF-8" /><title>v-once指令</title><!-- 引入Vue --><script type="text/javascript" src="../js/vue.js"></script></head><body><!-- v-once指令:1.v-once所在节点在初次动态渲染后,就视为静态内容了。2.以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能。--><!-- 准备好一个容器--><div id="root"><!-- 这个是初始化的,不用变 --><h2 v-once>初始化的n值是:{{n}}</h2>  <h2>当前的n值是:{{n}}</h2><button @click="n++">点我n+1</button></div></body><script type="text/javascript">Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。new Vue({el:'#root',data:{n:1}})</script>
</html>

15.5 v-pre指令(简单)

这个就是程序员写的是就是什么,不关vue的事

<!DOCTYPE html>
<html><head><meta charset="UTF-8" /><title>v-pre指令</title><!-- 引入Vue --><script type="text/javascript" src="../js/vue.js"></script></head><body><!-- v-pre指令:1.跳过其所在节点的编译过程。2.可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译。--><!-- 准备好一个容器--><div id="root"><h2 v-pre>Vue其实很简单</h2><h2 >当前的n值是:{{n}}</h2><button @click="n++">点我n+1</button></div></body><script type="text/javascript">Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。new Vue({el:'#root',data:{n:1}})</script>
</html>

16. 自定义属性

16.1 自定义指令

<!DOCTYPE html>
<html><head><meta charset="UTF-8" /><title>自定义指令</title><script type="text/javascript" src="../js/vue.js"></script></head><body><!-- 需求1:定义一个v-big指令,和v-text功能类似,但会把绑定的数值放大10倍。需求2:定义一个v-fbind指令,和v-bind功能类似,但可以让其所绑定的input元素默认获取焦点。自定义指令总结:一、定义语法:(1).局部指令:new Vue({                                                          new Vue({directives:{指令名:配置对象}   或          directives{指令名:回调函数}})                                                                      })(2).全局指令:Vue.directive(指令名,配置对象) 或   Vue.directive(指令名,回调函数)二、配置对象中常用的3个回调:(1).bind:指令与元素成功绑定时调用。(2).inserted:指令所在元素被插入页面时调用。(3).update:指令所在模板结构被重新解析时调用。三、备注:1.指令定义时不加v-,但使用时要加v-;2.指令名如果是多个单词,要使用kebab-case命名方式,不要用camelCase命名。--><!-- 准备好一个容器--><div id="root"><h2>{{name}}</h2><h2>当前的n值是:<span v-text="n"></span> </h2><!-- <h2>放大10倍后的n值是:<span v-big-number="n"></span> </h2> --><h2>放大10倍后的n值是:<span v-big="n"></span> </h2><button @click="n++">点我n+1</button><hr/><input type="text" v-fbind:value="n"></div></body><script type="text/javascript">Vue.config.productionTip = false//定义全局指令/* Vue.directive('fbind',{//指令与元素成功绑定时(一上来) bind(element,binding){element.value = binding.value},//指令所在元素被插入页面时inserted(element,binding){element.focus()},//指令所在的模板被重新解析时update(element,binding){element.value = binding.value}}) */new Vue({el:'#root',data:{name:'尚硅谷',n:1},directives:{//big函数何时会被调用?1.指令与元素成功绑定时(一上来)。2.指令所在的模板被重新解析时。/* 'big-number'(element,binding){// console.log('big')element.innerText = binding.value * 10}, */big(element,binding){  console.log('big',this) //注意此处的this是window// console.log('big')element.innerText = binding.value * 10},fbind:{//指令与元素成功绑定时(一上来)bind(element,binding){console.log(element,binding); element.value = binding.value},//指令所在元素被插入页面时inserted(element,binding){element.focus()},//指令所在的模板被重新解析时update(element,binding){element.value = binding.value}}}})</script>
</html>

16.2 回顾DOM的操作

<!DOCTYPE html>
<html><head><meta charset="UTF-8" /><title>Document</title><style>.demo{background-color: orange;}</style></head><body><button id="btn">点我创建一个输入框</button><script type="text/javascript" >const btn = document.getElementById('btn')btn.onclick = ()=>{const input = document.createElement('input')input.className = 'demo'input.value = 99input.onclick = ()=>{alert(1)}document.body.appendChild(input)input.focus()// input.parentElement.style.backgroundColor = 'skyblue'console.log(input.parentElement)}</script></body>
</html>

17 生命周期(目前先跳过去)

到时候有时间再看

17.1引出生命周期

需求,一开始就让页面的文字得到透明度逐渐发生变化,而不是通过点击或者触发。
使用方法的实现的话
用这个,不要将定时器写再方法中,可能会导致无限的调用,造成不必要的麻烦

<!DOCTYPE html>
<html><head><meta charset="UTF-8" /><title>引出生命周期</title><!-- 引入Vue --><script type="text/javascript" src="../js/vue.js"></script></head><body><!-- 生命周期:1.又名:生命周期回调函数、生命周期函数、生命周期钩子。2.是什么:Vue在关键时刻帮我们调用的一些特殊名称的函数。3.生命周期函数的名字不可更改,但函数的具体内容是程序员根据需求编写的。4.生命周期函数中的this指向是vm 或 组件实例对象。--><!-- 准备好一个容器--><div id="root"><h2 v-if="a">你好啊</h2><h2 :style="{opacity}">欢迎学习Vue</h2></div></body><script type="text/javascript">Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。new Vue({el:'#root',data:{a:false,opacity:1},methods: {},//Vue完成模板的解析并把初始的真实DOM元素放入页面后(挂载完毕)调用mountedmounted(){console.log('mounted',this)setInterval(() => {this.opacity -= 0.01if(this.opacity <= 0) this.opacity = 1},16)},})//通过外部的定时器实现(不推荐)/* setInterval(() => {vm.opacity -= 0.01if(vm.opacity <= 0) vm.opacity = 1},16) */</script>
</html>

17.2 分析生命周期

生命周期的图如下所示

17.3 总结生命周期

二 .Vue组件化编程

18.非单文件组件(不常用,跳过)

<!DOCTYPE html>
<html><head><meta charset="UTF-8" /><title>基本使用</title><script type="text/javascript" src="../js/vue.js"></script></head><body><!-- Vue中使用组件的三大步骤:一、定义组件(创建组件)二、注册组件三、使用组件(写组件标签)一、如何定义一个组件?使用Vue.extend(options)创建,其中options和new Vue(options)时传入的那个options几乎一样,但也有点区别;区别如下:1.el不要写,为什么? ——— 最终所有的组件都要经过一个vm的管理,由vm中的el决定服务哪个容器。2.data必须写成函数,为什么? ———— 避免组件被复用时,数据存在引用关系。备注:使用template可以配置组件结构。二、如何注册组件?1.局部注册:靠new Vue的时候传入components选项2.全局注册:靠Vue.component('组件名',组件)三、编写组件标签:<school></school>--><!-- 准备好一个容器--><div id="root"><hello></hello><hr><h1>{{msg}}</h1><hr><!-- 第三步:编写组件标签 --><school></school><hr><!-- 第三步:编写组件标签 --><student></student></div><div id="root2"><hello></hello></div></body><script type="text/javascript">Vue.config.productionTip = false//第一步:创建school组件const school = Vue.extend({template:`<div class="demo"><h2>学校名称:{{schoolName}}</h2><h2>学校地址:{{address}}</h2><button @click="showName">点我提示学校名</button>    </div>`,// el:'#root', //组件定义时,一定不要写el配置项,因为最终所有的组件都要被一个vm管理,由vm决定服务于哪个容器。data(){return {schoolName:'尚硅谷',address:'北京昌平'}},methods: {showName(){alert(this.schoolName)}},})//第一步:创建student组件const student = Vue.extend({template:`<div><h2>学生姓名:{{studentName}}</h2><h2>学生年龄:{{age}}</h2></div>`,data(){return {studentName:'张三',age:18}}})//第一步:创建hello组件const hello = Vue.extend({template:`<div>  <h2>你好啊!{{name}}</h2></div>`,data(){return {name:'Tom'}}})//第二步:全局注册组件Vue.component('hello',hello)//创建vmnew Vue({el:'#root',data:{msg:'你好啊!'},//第二步:注册组件(局部注册)components:{school,student}})new Vue({el:'#root2',})</script>
</html>

18.1 基本使用

<!DOCTYPE html>
<html><head><meta charset="UTF-8" /><title>几个注意点</title><script type="text/javascript" src="../js/vue.js"></script></head><body><!-- 几个注意点:1.关于组件名:一个单词组成:第一种写法(首字母小写):school第二种写法(首字母大写):School多个单词组成:第一种写法(kebab-case命名):my-school第二种写法(CamelCase命名):MySchool (需要Vue脚手架支持)备注:(1).组件名尽可能回避HTML中已有的元素名称,例如:h2、H2都不行。(2).可以使用name配置项指定组件在开发者工具中呈现的名字。2.关于组件标签:第一种写法:<school></school>第二种写法:<school/>备注:不用使用脚手架时,<school/>会导致后续组件不能渲染。3.一个简写方式:const school = Vue.extend(options) 可简写为:const school = options--><!-- 准备好一个容器--><div id="root"><h1>{{msg}}</h1><school></school></div></body><script type="text/javascript">Vue.config.productionTip = false//定义组件const s = Vue.extend({name:'atguigu',template:`<div><h2>学校名称:{{name}}</h2>  <h2>学校地址:{{address}}</h2>    </div>`,data(){return {name:'尚硅谷',address:'北京'}}})new Vue({el:'#root',data:{msg:'欢迎学习Vue!'},components:{school:s}})</script>
</html>

18.2 几个注意点

<!DOCTYPE html>
<html><head><meta charset="UTF-8" /><title>组件的嵌套</title><!-- 引入Vue --><script type="text/javascript" src="../js/vue.js"></script></head><body><!-- 准备好一个容器--><div id="root"></div></body><script type="text/javascript">Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。//定义student组件const student = Vue.extend({name:'student',template:`<div><h2>学生姓名:{{name}}</h2>   <h2>学生年龄:{{age}}</h2>    </div>`,data(){return {name:'尚硅谷',age:18}}})//定义school组件const school = Vue.extend({name:'school',template:`<div><h2>学校名称:{{name}}</h2>    <h2>学校地址:{{address}}</h2>    <student></student></div>`,data(){return {name:'尚硅谷',address:'北京'}},//注册组件(局部)components:{student}})//定义hello组件const hello = Vue.extend({template:`<h1>{{msg}}</h1>`,data(){return {msg:'欢迎来到尚硅谷学习!'}}})//定义app组件const app = Vue.extend({template:`<div>   <hello></hello><school></school></div>`,components:{school,hello}})//创建vmnew Vue({template:'<app></app>',el:'#root',//注册组件(局部)components:{app}})</script>
</html>

18.3 组件的嵌套

<!DOCTYPE html>
<html><head><meta charset="UTF-8" /><title>组件的嵌套</title><!-- 引入Vue --><script type="text/javascript" src="../js/vue.js"></script></head><body><!-- 准备好一个容器--><div id="root"></div></body><script type="text/javascript">Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。//定义student组件const student = Vue.extend({name:'student',template:`<div><h2>学生姓名:{{name}}</h2>   <h2>学生年龄:{{age}}</h2>    </div>`,data(){return {name:'尚硅谷',age:18}}})//定义school组件const school = Vue.extend({name:'school',template:`<div><h2>学校名称:{{name}}</h2>    <h2>学校地址:{{address}}</h2>    <student></student></div>`,data(){return {name:'尚硅谷',address:'北京'}},//注册组件(局部)components:{student}})//定义hello组件const hello = Vue.extend({template:`<h1>{{msg}}</h1>`,data(){return {msg:'欢迎来到尚硅谷学习!'}}})//定义app组件const app = Vue.extend({template:`<div>   <hello></hello><school></school></div>`,components:{school,hello}})//创建vmnew Vue({template:'<app></app>',el:'#root',//注册组件(局部)components:{app}})</script>
</html>

18.4 VueComponent

<!DOCTYPE html>
<html><head><meta charset="UTF-8" /><title>VueComponent</title><script type="text/javascript" src="../js/vue.js"></script></head><body><!-- 关于VueComponent:1.school组件本质是一个名为VueComponent的构造函数,且不是程序员定义的,是Vue.extend生成的。2.我们只需要写<school/>或<school></school>,Vue解析时会帮我们创建school组件的实例对象,即Vue帮我们执行的:new VueComponent(options)。3.特别注意:每次调用Vue.extend,返回的都是一个全新的VueComponent!!!!4.关于this指向:(1).组件配置中:data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是【VueComponent实例对象】。(2).new Vue(options)配置中:data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是【Vue实例对象】。5.VueComponent的实例对象,以后简称vc(也可称之为:组件实例对象)。Vue的实例对象,以后简称vm。--><!-- 准备好一个容器--><div id="root"><school></school><hello></hello></div></body><script type="text/javascript">Vue.config.productionTip = false//定义school组件const school = Vue.extend({name:'school',template:`<div><h2>学校名称:{{name}}</h2> <h2>学校地址:{{address}}</h2>    <button @click="showName">点我提示学校名</button></div>`,data(){return {name:'尚硅谷',address:'北京'}},methods: {showName(){console.log('showName',this)}},})const test = Vue.extend({template:`<span>atguigu</span>`})//定义hello组件const hello = Vue.extend({template:`<div><h2>{{msg}}</h2><test></test>    </div>`,data(){return {msg:'你好啊!'}},components:{test}})// console.log('@',school)// console.log('#',hello)//创建vmconst vm = new Vue({el:'#root',components:{school,hello}})</script>
</html>

18.5 一个重要的内置关系

<!DOCTYPE html>
<html><head><meta charset="UTF-8" /><title>一个重要的内置关系</title><!-- 引入Vue --><script type="text/javascript" src="../js/vue.js"></script></head><body><!-- 1.一个重要的内置关系:VueComponent.prototype.__proto__ === Vue.prototype2.为什么要有这个关系:让组件实例对象(vc)可以访问到 Vue原型上的属性、方法。--><!-- 准备好一个容器--><div id="root"><school></school></div></body><script type="text/javascript">Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。Vue.prototype.x = 99//定义school组件const school = Vue.extend({name:'school',template:`<div><h2>学校名称:{{name}}</h2>   <h2>学校地址:{{address}}</h2>    <button @click="showX">点我输出x</button></div>`,data(){return {name:'尚硅谷',address:'北京'}},methods: {showX(){console.log(this.x)}},})//创建一个vmconst vm = new Vue({el:'#root',data:{msg:'你好'},components:{school}})//定义一个构造函数/* function Demo(){this.a = 1this.b = 2}//创建一个Demo的实例对象const d = new Demo()console.log(Demo.prototype) //显示原型属性console.log(d.__proto__) //隐式原型属性console.log(Demo.prototype === d.__proto__)//程序员通过显示原型属性操作原型对象,追加一个x属性,值为99Demo.prototype.x = 99console.log('@',d) */</script>
</html>

19.单文件组件

一个标准的单文件组件
School.vue文件

<template><div class="demo"><h2>学校名称:{{ schoolName }}</h2><h2>学校地址:{{ address }}</h2><button @click="showName">点我提示学校名</button></div>
</template><script>export default ({   //为了可以让其它的文件可以引用 一般都喜欢用默认暴露name:'School',  //最好和文件名保持一致data() {return {schoolName: "尚硅谷",address: "北京昌平",};},methods: {showName() {alert(this.schoolName);},},
});
</script><style>.demo{background-color: aqua;}
</style>

index.htm文件

<!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="root"></div><script src="../js/vue.js"></script><script src="./main.js"></script>
</body>
</html>

main.js文件

import App from './School.vue'new Vue({el:"#root",template:`<App></App>`,components:{App}
})

School.vue文件

<template><div class="demo"><h2>学校名称:{{ schoolName }}</h2><h2>学校地址:{{ address }}</h2><button @click="showName">点我提示学校名</button></div>
</template><script>export default ({   //为了可以让其它的文件可以引用 一般都喜欢用默认暴露name:'School',  //最好和文件名保持一致data() {return {schoolName: "尚硅谷",address: "北京昌平",};},methods: {showName() {alert(this.schoolName);},},
});
</script><style>.demo{background-color: aqua;}
</style>

Student.vue文件

<template><div class="demo"><h2>学生姓名:{{ name }}</h2><h2>学生年龄:{{ age }}</h2></div>
</template><script>export default ({name:'School',data() {return {name: "鲸落",age: 45,};},
});
</script>

三 vue脚手架

创建项目

20.脚手架文件的分析

脚手架文件结构

├── node_modules
├── public
│   ├── favicon.ico: 页签图标
│   └── index.html: 主页面
├── src
│   ├── assets: 存放静态资源
│   │   └── logo.png
│   │── component: 存放组件
│   │   └── HelloWorld.vue
│   │── App.vue: 汇总所有组件
│   │── main.js: 入口文件
├── .gitignore: git版本管制忽略的配置
├── babel.config.js: babel的配置文件
├── package.json: 应用包配置文件
├── README.md: 应用描述文件
├── package-lock.json:包版本控制文件

package.json文件
··




main.js文件

/* 该文件是整个项目的入口文件
*/
//引入Vue
import Vue from 'vue'
//引入App组件,它是所有组件的父组件
import App from './App.vue'
//关闭vue的生产提示
Vue.config.productionTip = false/* 关于不同版本的Vue:1.vue.js与vue.runtime.xxx.js的区别:(1).vue.js是完整版的Vue,包含:核心功能+模板解析器。(2).vue.runtime.xxx.js是运行版的Vue,只包含:核心功能;没有模板解析器。2.因为vue.runtime.xxx.js没有模板解析器,所以不能使用template配置项,需要使用render函数接收到的createElement函数去指定具体内容。
*///创建Vue实例对象---vm
new Vue({el:'#app',//render函数完成了这个功能:将App组件放入容器中render: h => h(App),// render:q=> q('h1','你好啊')// template:`<h1>你好啊</h1>`,// components:{App},
})

21 ref属性(ok)





运行结果

总结:
ref属性

  1. 被用来给元素或子组件注册引用信息(id的替代者)
  2. 应用在html标签上获取的是真实DOM元素,应用在组件标签上是组件实例对象(vc)
  3. 使用方式:
    1. 打标识:<h1 ref="xxx">.....</h1><School ref="xxx"></School>
    2. 获取:this.$refs.xxx

22 props属性(ok)

本节用到的文件如下
简单的父组件给子组件传值的方式

本节用到是上面的两个文件
StudentStudent.vue文件

<template><div class="demo"><h1>{{msg}}</h1><h2>学校的名称:{{name}}</h2><h2>学校地址:{{sex}}</h2><h2>学生年龄:{{age+22}}</h2></div>
</template><script>
export default {name:'StudentStudent',data() {return {msg:"我是一个优秀的学生"}},props:["name","sex","age"]
}
</script><style>.demo{background-color: aquamarine;}
</style>

App.vue文件

<template>
<div><StudentStudent name="小白" sex="女" age="22"/><StudentStudent name="李百" sex="男" :age="55"/></div>
</template><script>
// 引入school组件
import StudentStudent from './components/StudentStudent'export default {name:'App',components:{StudentStudent},
}</script><style></style>

运行结果


限制类型的方式


更高端的方式

总结
props配置项

  1. 功能:让组件接收外部传过来的数据

  2. 传递数据:<Demo name="xxx"/>

  3. 接收数据:

    1. 第一种方式(只接收):props:['name']

    2. 第二种方式(限制类型):props:{name:String}

    3. 第三种方式(限制类型、限制必要性、指定默认值):

      props:{name:{type:String, //类型required:true, //必要性default:'老王' //默认值}
      }
      

    备注:props是只读的,Vue底层会监测你对props的修改,如果进行了修改,就会发出警告,若业务需求确实需要修改,那么请复制props的内容到data中一份,然后去修改data中的数据。



23 mixin混入(合)

为什么要引入混合
如下图代码

有重复的部分,看起来很不舒服
所以有必要将它们合在一起不是更好
所以就有了这个概念


总结:

  1. 功能:可以把多个组件共用的配置提取成一个混入对象

  2. 使用方式:

    第一步定义混合:

    {data(){....},methods:{....}....
    }
    

    第二步使用混入:

    ​ 全局混入:Vue.mixin(xxx)
    ​ 局部混入:mixins:['xxx']

24 插件(简单的东西)(很重要)



插件在这里定义

引入插件

25 scoped样式


26 TodoList案例

本节主要实现的效果如下


总共分成四个vue的组件

静态布局

关键代码

MyFooter.vue

MyHeader.vue

MyItem.vue

MyList.vue

完整代码

<template><div class="todo-footer"><label><input type="checkbox"/></label><span><span></span> / 全部</span><button class="btn btn-danger">清除已完成任务</button></div>
</template><script>export default {name:'MyFooter',}
</script><style scoped>/*footer*/.todo-footer {height: 40px;line-height: 40px;padding-left: 6px;margin-top: 5px;}.todo-footer label {display: inline-block;margin-right: 20px;cursor: pointer;}.todo-footer label input {position: relative;top: -1px;vertical-align: middle;margin-right: 5px;}.todo-footer button {float: right;margin-top: 5px;}
</style>
<template><div class="todo-header"><input type="text" placeholder="请输入你的任务名称,按回车键确认"/></div>
</template><script>export default {name:'MyHeader',}
</script><style scoped>/*header*/.todo-header input {width: 560px;height: 28px;font-size: 14px;border: 1px solid #ccc;border-radius: 4px;padding: 4px 7px;}.todo-header input:focus {outline: none;border-color: rgba(82, 168, 236, 0.8);box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);}
</style>
<template><li><label><input type="checkbox"/><span>{{todo.title}}</span></label><button>删除</button></li>
</template><script>export default {name:'MyItem',// 声明接收的todo对象props:['todo'],}
</script><style scoped>/*item*/li {list-style: none;height: 36px;line-height: 36px;padding: 0 5px;border-bottom: 1px solid #ddd;}li label {float: left;cursor: pointer;}li label li input {vertical-align: middle;margin-right: 6px;position: relative;top: -1px;}li button {float: right;display: none;margin-top: 3px;}li:before {content: initial;}li:last-child {border-bottom: none;}li:hover{background-color: #ddd;}li:hover button{display: block;}
</style>
<template><ul class="todo-main"><MyItem v-for="todoObj in todos" :key="todoObj.id" :todo="todoObj"/></ul>
</template><script>import MyItem from './MyItem'export default {name:'MyList',components:{MyItem},data() {return {todos:[{id:'0001',title:'吃饭',deno:true},{id:'0002',title:'喝酒',deno:false},{id:'0003',title:'开车',deno:true},]}},}
</script><style scoped>/*main*/.todo-main {margin-left: 0px;border: 1px solid #ddd;border-radius: 2px;padding: 0px;}.todo-empty {height: 40px;line-height: 40px;border: 1px solid #ddd;border-radius: 2px;padding-left: 5px;margin-top: 10px;}
</style>
<template><div id="root"><div class="todo-container"><div class="todo-wrap"><MyHeader/><MyList/><MyFooter/></div></div></div>
</template><script>import MyHeader from './components/MyHeader'import MyList from './components/MyList'import MyFooter from './components/MyFooter.vue'export default {name:'App',  //自己的名字components:{MyHeader,MyList,MyFooter},}
</script><style>/*base*/body {background: #fff;}.btn {display: inline-block;padding: 4px 12px;margin-bottom: 0;font-size: 14px;line-height: 20px;text-align: center;vertical-align: middle;cursor: pointer;box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);border-radius: 4px;}.btn-danger {color: #fff;background-color: #da4f49;border: 1px solid #bd362f;}.btn-danger:hover {color: #fff;background-color: #bd362f;}.btn:focus {outline: none;}.todo-container {width: 600px;margin: 0 auto;}.todo-container .todo-wrap {padding: 10px;border: 1px solid #ddd;border-radius: 5px;}
</style>
//引入Vue
import Vue from 'vue'
//引入App
import App from './App.vue'
//关闭Vue的生产提示
Vue.config.productionTip = false//创建vm
new Vue({el:'#app',render: h => h(App)
})

项目的过程细节

实现勾选



动态确定勾选谁

添加一个todo

需求:在在输入框中输入内容的时候,按回车就会把内容添加到列表里面。

第一阶段在输入框中输入内容

运行结果

拓展

在本节需要使用到id,因此引入了一个库,它可以随机的生成不重复的id
导入它的方式:


下载它的方式如下

总结:在这里面实现了输入并存放数据的功能,本节主要使用知识点有

  • @keyup.enter="add"通过点击键盘中的每一个键触发,这里使用的是回车键
  • e.target.value获取输入框输入的值
  • 随机生成idnanoid的下载和使用

将添加的东西传给另一个组件


本次动了下面的三个文件
Myheader.vue
Mylist.vue
App.vue
App.vue将数据通过props传给Mylist.vue组件,然后App.vue组件通过传递一个函数给Myheader.vue通过参数的方式得到它输入的数据
Mylist.vue

App.vue


Myheader.vue


上面传递成功了

可以真的添加到列表里面了

MyHeader.vue组件变化的地方
·
App.vue变化的地方

勾选列表(引起数据的变化)


App.vue
变化的地方

MyList.vue变数的地方

MyItem.vue组件变化的地方

运行结果
没有点击之前的状态

点击之后的状态

删除按钮的实现

MyItem.vue组件的变化

MyList.vue组件的变化

App.vue组件的变化



底部的统计功能的实现

本节会用到reduce这个方法reduce的回顾
reduce这个方法作用在数组上,数组有几条数据就会执行几次



pre第一次的值为后面给的第二个参数0,第二个参数为第一次的返回值
有于没有写retrun语句,所以后面的两个值都是未定义
当给一个返回值的时候


current为每一个数组的每一个项



进入到正题
App.vue改变的地方

MyFooter.vue改变的地方

运行结果


底部交互功能的实现

实现了上面全部勾选上的时候下面的全选按钮就会被勾选上


勾选下面的按钮,上面的按钮会全部被勾选



底部的全部清除功能


总结TodoList案例

  1. 组件化编码流程:

    ​ (1).拆分静态组件:组件要按照功能点拆分,命名不要与html元素冲突。

    ​ (2).实现动态组件:考虑好数据的存放位置,数据是一个组件在用,还是一些组件在用:

    ​ 1).一个组件在用:放在组件自身即可。

    ​ 2). 一些组件在用:放在他们共同的父组件上(状态提升)。

    ​ (3).实现交互:从绑定事件开始。

  2. props适用于:

    ​ (1).父组件 ==> 子组件 通信

    ​ (2).子组件 ==> 父组件 通信(要求父先给子一个函数)

  3. 使用v-model时要切记:v-model绑定的值不能是props传过来的值,因为props是不可以修改的!

  4. props传过来的若是对象类型的值,修改对象中的属性时Vue不会报错,但不推荐这样做。

27.浏览器本地存储(比较轻松)




存数据

<!DOCTYPE html>
<html><head><meta charset="utf-8"><title>localStorage</title></head><body><h2>localStorage</h2><button onclick="saveData()">点我保存一个数据</button></body><script type="text/javascript">let p = {name:'张三',age:13}function saveData(){localStorage.setItem('msg','hello!!!')localStorage.setItem('msg2',666)localStorage.setItem('person',JSON.stringify(p))//将一个对象装换成一个字符串并且可以正常的显示}</script>
</html>


读数据


移除和清空数据

将浏览器关闭掉也不会丢失
清空缓存就没有了

session

<!DOCTYPE html>
<html><head><meta charset="UTF-8" /><title>sessionStorage</title></head><body><h2>sessionStorage</h2><button onclick="saveData()">点我保存一个数据</button><button onclick="readData()">点我读取一个数据</button><button onclick="deleteData()">点我删除一个数据</button><button onclick="deleteAllData()">点我清空一个数据</button><script type="text/javascript" >let p = {name:'张三',age:18}function saveData(){sessionStorage.setItem('msg','hello!!!')sessionStorage.setItem('msg2',666)sessionStorage.setItem('person',JSON.stringify(p))}function readData(){console.log(sessionStorage.getItem('msg'))console.log(sessionStorage.getItem('msg2'))const result = sessionStorage.getItem('person')console.log(JSON.parse(result))// console.log(sessionStorage.getItem('msg3'))}function deleteData(){sessionStorage.removeItem('msg2')}function deleteAllData(){sessionStorage.clear()}</script></body>
</html>

总结

webStorage

  1. 存储内容大小一般支持5MB左右(不同浏览器可能还不一样)

  2. 浏览器端通过 Window.sessionStorage 和 Window.localStorage 属性来实现本地存储机制。

  3. 相关API:

    1. xxxxxStorage.setItem('key', 'value');
      该方法接受一个键和值作为参数,会把键值对添加到存储中,如果键名存在,则更新其对应的值。

    2. xxxxxStorage.getItem('person');

      ​ 该方法接受一个键名作为参数,返回键名对应的值。

    3. xxxxxStorage.removeItem('key');

      ​ 该方法接受一个键名作为参数,并把该键名从存储中删除。

    4. xxxxxStorage.clear()

      ​ 该方法会清空存储中的所有数据。

  4. 备注:

    1. SessionStorage存储的内容会随着浏览器窗口关闭而消失。
    2. LocalStorage存储的内容,需要手动清除才会消失。
    3. xxxxxStorage.getItem(xxx)如果xxx对应的value获取不到,那么getItem的返回值是null。
    4. JSON.parse(null)的结果依然是null。

28.todoList_本地存储



刷新浏览器不会丢失数据

29 组件的自定义事件

这个是为了区别内置事件而产生的
本节用到三个文件
School.vue文件

<template><div class="school"><h2>学校名称:{{name}}</h2><h2>学校地址:{{address}}</h2></div>
</template><script>export default {name:'School',data() {return {name:'尚硅谷',address:'北京',}},}
</script><style scoped>.school{background-color: skyblue;padding: 5px;}
</style>

Student.vue文件

<template><div class="student"><h2>学生姓名:{{name}}</h2><h2>学生性别:{{sex}}</h2></div>
</template><script>export default {name:'Student',data() {return {name:'张三',sex:'男'}},}
</script><style lang="less" scoped>.student{background-color: pink;padding: 5px;margin-top: 30px;}
</style>

app.vue

<template><div class="app"><h1>{{msg}}</h1><School/><Student/></div>
</template><script>import Student from './components/Student'import School from './components/School'export default {name:'App',components:{School,Student},data(){return{msg:"你好啊"}}}
</script><style scoped>.app{background-color: gray;padding: 5px;}
</style>



父给子传递函数的方式获取子的数据(这是之前用到过的)为了和下面的形成对比



下面通过app.vue和Student.vue文件将下面的组件的自定义事件

App.vue文件

Student.vue文件


第二种方式,比第一种更加的灵活

如果想要传多个值的话
student.vue

app.vue


解绑自定义事件

解绑一个自定义事件

销毁多个

组件自定义事件的总结



组件绑定原生的事件


会放在最外侧的那一个大的div,所以这就是只要一个根元素的原因之一

总结

  1. 一种组件间通信的方式,适用于:子组件 ===> 父组件

  2. 使用场景:A是父组件,B是子组件,B想给A传数据,那么就要在A中给B绑定自定义事件(事件的回调在A中)。

  3. 绑定自定义事件:

    1. 第一种方式,在父组件中:<Demo @atguigu="test"/><Demo v-on:atguigu="test"/>

    2. 第二种方式,在父组件中:

      <Demo ref="demo"/>
      ......
      mounted(){this.$refs.xxx.$on('atguigu',this.test)
      }
      
    3. 若想让自定义事件只能触发一次,可以使用once修饰符,或$once方法。

TodoList都改成成自定义事件的方式(子给父传递)

App.vue组件子组件header.vue通过自定义事件的方式拿到子组件的数据


Footer.vue的改变

全局事件总线(重要)

可以实现任意组件之间的通信

兄弟组件之间的通信(传数据)




总结:
全局事件总线(GlobalEventBus)

  1. 一种组件间通信的方式,适用于任意组件间通信。

  2. 安装全局事件总线:

    new Vue({......beforeCreate() {Vue.prototype.$bus = this //安装全局事件总线,$bus就是当前应用的vm},......
    })
    
  3. 使用事件总线:

    1. 接收数据:A组件想接收数据,则在A组件中给$bus绑定自定义事件,事件的回调留在A组件自身。

      methods(){demo(data){......}
      }
      ......
      mounted() {this.$bus.$on('xxxx',this.demo)
      }
      
    2. 提供数据:this.$bus.$emit('xxxx',数据)

TodoList案例_事件总线

本节改的是App.vueMyItemvue通信的情况
下面两张都是App.vue


MyList.vue文件

消息订阅与发布_pubsub(另外一种通信的方式)(这个在vue里面不常用)

本节需要用到的库
pubsub.js他是subscript和publish的简写(在任何一个框架里面都可以实现消息的发布和订阅
)
先安装这个库


本节可以通过消息订阅的方式实现两个组件的通信
student.vue




用完了就要解绑
总结

== 消息订阅与发布(pubsub)==

  1. 一种组件间通信的方式,适用于任意组件间通信。

  2. 使用步骤:

    1. 安装pubsub:npm i pubsub-js

    2. 引入: import pubsub from 'pubsub-js'

    3. 接收数据:A组件想接收数据,则在A组件中订阅消息,订阅的回调留在A组件自身。

      methods(){demo(data){......}
      }
      ......
      mounted() {this.pid = pubsub.subscribe('xxx',this.demo) //订阅消息
      }
      
    4. 提供数据:pubsub.publish('xxx',数据)

    5. 最好在beforeDestroy钩子中,用PubSub.unsubscribe(pid)去取消订阅。

动画

<template><div><button @click="isShow =!isShow">显示/隐藏</button><transition><h1 v-show="isShow">你好啊!</h1></transition></div>
</template><script>
export default {data(){return {isShow:true}},
}
</script><style>
h1{background-color: orange;
}
.v-enter-active{animation: atguigu 1s;
}
.v-leave-active{animation: atguigu 1s reverse;
}
@keyframes atguigu{from{transform: translateX(-100%);},to{transform: translateX(0px);}
}
</style>

可以让他自己做动画

过渡效果
<template><div><button @click="isShow = !isShow">显示/隐藏</button><transition name="hello" appear><h1 v-show="isShow">你好啊!</h1></transition><!-- 有多个的时候可以给transition取名字 --></div>
</template><script>
export default {name:'Test2',data() {return {isShow: true,};},
};
</script><style>
h1 {background-color: orange;transition: 0.5s linear;
}
/* 进入的起点 */
.hello-enter{transform: translateX(-100%);
}
/* 进入的终点 */
.hello-enter-to {transform: translateX(0);
}
/* 离开的起点 */
.hello-leave{transform: translateX(0);
}
/* 离开的终点 */
.hello-leave-to{transform: translateX(-100%);
}</style>

剩下的需要的时候再说(以后用到的时候再回来)

四 vue中的ajax

30 .配置代理_方式一



开启一个服务器





服务器收到了请求

不过这种方式的一次只能向一个端口请求数据



这种方式有一种小小的不完美

32 置代理_方式二



总结

vue脚手架配置代理

方法一

​ 在vue.config.js中添加如下配置:

devServer:{proxy:"http://localhost:5000"
}

说明:

  1. 优点:配置简单,请求资源时直接发给前端(8080)即可。
  2. 缺点:不能配置多个代理,不能灵活的控制请求是否走代理。
  3. 工作方式:若按照上述配置代理,当请求了前端不存在的资源时,那么该请求会转发给服务器 (优先匹配前端资源)

方法二

​ 编写vue.config.js配置具体代理规则:

module.exports = {devServer: {proxy: {'/api1': {// 匹配所有以 '/api1'开头的请求路径target: 'http://localhost:5000',// 代理目标的基础路径changeOrigin: true,pathRewrite: {'^/api1': ''}},'/api2': {// 匹配所有以 '/api2'开头的请求路径target: 'http://localhost:5001',// 代理目标的基础路径changeOrigin: true,pathRewrite: {'^/api2': ''}}}}
}
/*changeOrigin设置为true时,服务器收到的请求头中的host为:localhost:5000changeOrigin设置为false时,服务器收到的请求头中的host为:localhost:8080changeOrigin默认值为true
*/

说明:

  1. 优点:可以配置多个代理,且可以灵活的控制请求是否走代理。
  2. 缺点:配置略微繁琐,请求资源时必须加前缀。

33 github案例_静态组件

github用户搜索案例

静态效果

要实现的效果如下

目录结构

bootstrap.css文件的地址为:地址
它的引入地方

上面的第三方的样式库是控制Search.vue这个组件的
List.vue

<template><div class="row"><div class="card"><a href="https://github.com/xxxxxx" target="_blank"><imgsrc="https://cn.vuejs.org/images/logo.svg"style="width: 100px"/></a><p class="card-text">xxxxxx</p></div><div class="card"><a href="https://github.com/xxxxxx" target="_blank"><imgsrc="https://cn.vuejs.org/images/logo.svg"style="width: 100px"/></a><p class="card-text">xxxxxx</p></div><div class="card"><a href="https://github.com/xxxxxx" target="_blank"><imgsrc="https://cn.vuejs.org/images/logo.svg"style="width: 100px"/></a><p class="card-text">xxxxxx</p></div><div class="card"><a href="https://github.com/xxxxxx" target="_blank"><imgsrc="https://cn.vuejs.org/images/logo.svg"style="width: 100px"/></a><p class="card-text">xxxxxx</p></div><div class="card"><a href="https://github.com/xxxxxx" target="_blank"><imgsrc="https://cn.vuejs.org/images/logo.svg"style="width: 100px"/></a><p class="card-text">xxxxxx</p></div></div>
</template><script>
export default {name: "List",
};
</script><style scoped>
.album {min-height: 50rem; /* Can be removed; just added for demo purposes */padding-top: 3rem;padding-bottom: 3rem;background-color: #f7f7f7;
}.card {float: left;width: 33.333%;padding: 0.75rem;margin-bottom: 2rem;border: 1px solid #efefef;text-align: center;
}.card > img {margin-bottom: 0.75rem;border-radius: 100px;
}.card-text {font-size: 85%;
}
</style>

Search.vue

<template><section class="jumbotron"><h3 class="jumbotron-heading">Search Github Users</h3><div><inputtype="text"placeholder="enter the name you search"/>&nbsp;<button>Search</button></div></section>
</template><script>
export default {name: "Search",
};
</script>

App.vue

<template><div class="container"><Search /><List /></div>
</template><script>
import Search from "./components/Search.vue";
import List from "./components/List.vue";export default {name: "App",components: { Search, List },
};
</script><style>
</style>

静态效果(这个时候还没有绑定数据)

添加数据之后

https://api.github.com/search/users?q=xxx这个是github官方免费提供的接口
本节在上节的基础上只变了下面的两个文件


List.vue文件

<template><div class="row"><div class="card" v-for="user in users" :key="user.login"><a :href="user.html_url" target="_blank"><img :src="user.avatar_url" style="width:100px"/></a> <p class="card-text">{{user.login}}</p></div>   </div>
</template><script>
export default {name: "List",data() {return {users:[]}},mounted(){this.$bus.$on('data1',(users)=>{this.users = users;})},beforeDestroy(){this.$bus.$off('data1');}
};
</script><style scoped>
.album {min-height: 50rem; /* Can be removed; just added for demo purposes */padding-top: 3rem;padding-bottom: 3rem;background-color: #f7f7f7;
}.card {float: left;width: 33.333%;padding: 0.75rem;margin-bottom: 2rem;border: 1px solid #efefef;text-align: center;
}.card > img {margin-bottom: 0.75rem;border-radius: 100px;
}.card-text {font-size: 85%;
}
</style>

sreach.vue文件

<template><section class="jumbotron"><h3 class="jumbotron-heading">Search Github Users</h3><div><inputtype="text"placeholder="enter the name you search" v-model="keyWord"/>&nbsp;<button @click="searchUsers">Search</button></div></section>
</template><script>// 导入axios,之前已经下过了
import axios from 'axios'
export default {name: "Search",data() {return {keyWord:''}},methods:{// 通过点击按钮向github发送请求searchUsers(){// https://api.github.com/search/users?q=xxx github免费提供的接口axios.get(`https://api.github.com/search/users?q=${this.kayWord}`).then(response=>{// console.log("请求成功了",response.data);console.log(response.data.items);this.$bus.$emit("data1",response.data.items); //发送数据},error=>{console.log("失败",error.message);})}}
};
</script>


这里面不存在跨域问题
github的后端已经在
运行结果

完善案例

代码下多写几遍之后再写过来

33.vue-resource(这个插件现在用到比较少)(了解就可)

它在1.0的时候用的比较多
常用的方式

vue-resource是对于xhr的封装
安装


34. slot 插槽

34.1默认插槽


用到了上面的两个文件夹
App.vue的代码
Appv.ue

<template><div class="container"><Category><img src="../public/1.jpg" alt="" /></Category><Category><ul><li v-for="(g, index) in games" :key="index">{{g}}</li></ul></Category><Category><video controls src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"></video></Category></div>
</template><script>
import Category from "./components/Category";export default {name: "App",components: { Category },data() {return {foods: ["火锅", "烧烤", "小龙虾", "牛排"],games: ["红色警戒", "穿越火线", "劲舞团", "超级玛丽"],films: ["《教父》","《拆弹专家》","《你好,李焕英》","《尚硅谷》",],};},
};
</script><style scope>
.container {display: flex;justify-content: space-around;
}
img {width: 100%;height: 120px;
}
video{width: 100%;
}
</style>

category.vue

<template><div class="category"><h3>vvvv</h3><!-- 定义一个插槽(挖个坑,等着组件的使用者进行填充) --><slot>我是一些默认值,当使用者没有传递具体结构时,我会出现</slot></div>
</template><script>
export default {name:'Catsgory'
}
</script><style>
.category{width   : 200px;height: 200px;background-color: skyblue;
}
h3{text-align: center;background-color: orange;
}
</style>

运行结果

注意

34.2 具名插槽

本节要实现的效果如下


App.vue

<template><div class="container"><Category title="美食"><img slot="center" src="../public/1.jpg" alt="" /><a class="foot" slot="footer" href="http://www.baidu.com">更多美食</a></Category><Category title="游戏"><ul slot="center"><li v-for="(g, index) in games" :key="index">{{ g }}</li></ul><div slot="footer" class="foot"><a href="http://www.baidu.com">单机游戏</a><a href="http://www.baidu.com">网络游戏</a></div></Category><Category title="电影"><videocontrolssrc="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"slot="center"></video><template slot="footer"><div  class="foot"><a href="http://www.baidu.com">经典</a><a href="http://www.baidu.com">热门</a><a href="http://www.baidu.com">推荐</a></div><h4 slot="footer">欢迎前来观看</h4></template></Category></div>
</template><script>
import Category from "./components/Category";export default {name: "App",components: { Category },data() {return {foods: ["火锅", "烧烤", "小龙虾", "牛排"],games: ["红色警戒", "穿越火线", "劲舞团", "超级玛丽"],films: ["《教父》","《拆弹专家》","《你好,李焕英》","《尚硅谷》",],};},
};
</script><style scope>
.container,.foot{display: flex;justify-content: space-around;
}
img {width: 100%;height: 120px;
}
video {width: 100%;
}
h4 {text-align: center;
}
</style>

category.vue

<template><div class="category"><h3>{{title}}分类</h3><!-- 定义一个插槽(挖个坑,等着组件的使用者进行填充) --><slot name="center">我是一些默认值,当使用者没有传递具体结构时,我会出现</slot><slot name="footer">我是一些默认值,当使用者没有传递具体结构时,我会出现</slot></div>
</template><script>
export default {name:'Category',props:['title']
}
</script><style>
.category{width   : 200px;height: 250px;background-color: skyblue;
}
h3{text-align: center;background-color: orange;
}
</style>

运行结果

代码说明

34.3 作用域插槽(不是很好理解)

分析


本节用到的两个文件

category.vue

<template><div class="category"><h3>{{ title }}分类</h3><!-- 定义一个插槽(挖个坑,等着组件的使用者进行填充) --><slot :games="games" msg="hello">我是一些默认值,当使用者没有传递具体结构时,我会出现</slot></div></template><script>
export default {name: "Category",props: ["title"],data() {return {games:['红色警戒','穿越火线','劲舞团','超级玛丽']}},
};
</script><style>
.category {width: 200px;height: 250px;background-color: skyblue;
}
h3 {text-align: center;background-color: orange;
}
</style>

App.vue

<template><div class="container"><Category title="游戏"><template slot-scope="weijiang"><ul><li v-for="(g, index) in weijiang.games" :key="index">{{ g }}</li></ul></template></Category><Category title="游戏"><template slot-scope="weijiang"><ol><li style="color:red" v-for="(g, index) in weijiang.games" :key="index">{{ g }}</li></ol><h4>{{weijiang.msg}}</h4></template></Category><Category title="游戏"><template slot-scope="{games}"><h4 v-for="(g, index) in games" :key="index">{{ g }}</h4></template></Category></div>
</template><script>
import Category from "./components/Category";export default {name: "App",components: { Category },
};
</script><style scope>
.container,
.foot {display: flex;justify-content: space-around;
}
img {width: 100%;height: 120px;
}
video {width: 100%;
}
h4 {text-align: center;
}
</style>

运行结果

代码解释

33.4 总结

  1. 作用:让父组件可以向子组件指定位置插入html结构,也是一种组件间通信的方式,适用于 父组件 ===> 子组件

  2. 分类:默认插槽、具名插槽、作用域插槽

  3. 使用方式:

    1. 默认插槽:

      父组件中:<Category><div>html结构1</div></Category>
      子组件中:<template><div><!-- 定义插槽 --><slot>插槽默认内容...</slot></div></template>
      
    2. 具名插槽:

      父组件中:<Category><template slot="center"><div>html结构1</div></template><template v-slot:footer><div>html结构2</div></template></Category>
      子组件中:<template><div><!-- 定义插槽 --><slot name="center">插槽默认内容...</slot><slot name="footer">插槽默认内容...</slot></div></template>
      
    3. 作用域插槽:

      1. 理解:数据在组件的自身,但根据数据生成的结构需要组件的使用者来决定。(games数据在Category组件中,但使用数据所遍历出来的结构由App组件决定)

      2. 具体编码:

        父组件中:<Category><template scope="scopeData"><!-- 生成的是ul列表 --><ul><li v-for="g in scopeData.games" :key="g">{{g}}</li></ul></template></Category><Category><template slot-scope="scopeData"><!-- 生成的是h4标题 --><h4 v-for="g in scopeData.games" :key="g">{{g}}</h4></template></Category>
        子组件中:<template><div><slot :games="games"></slot></div></template><script>export default {name:'Category',props:['title'],//数据在子组件自身data() {return {games:['红色警戒','穿越火线','劲舞团','超级玛丽']}},}</script>
        

超级详细的vue2学习笔记相关推荐

  1. 超详细!Vue-coderwhy个人学习笔记(二)(Day3)

    前言 本文章接上一篇笔记 超详细!Vue-coderwhy个人学习笔记(一)(Day1-Day2) 这篇主要是Day3笔记,组件化,组件通信,插槽 四.组件化开发 (一).内容概述 认识组件化 注册组 ...

  2. Vue2学习笔记1 - win10下安装vue开发环境

    操作系统为Win10_x64 1.安装NodeJs Vue的安装依赖NodeJs,所以需要先安装NodeJs,去NodeJs官网(http://nodejs.cn/download/)下载对应系统的n ...

  3. 尚硅谷Vue2学习笔记分享

    前言 这里是尚硅谷Vue2的学习笔记分享. 原视频是尚硅谷Vue2.0+Vue3.0全套教程丨vuejs从入门到精通 Vue3的笔记链接 文章目录 前言 初识Vue 模板语法 数据绑定 el和data ...

  4. 【网速】Visual Studio 下载太慢的问题的解决办法【超详细,来源于学习笔记】

    Visual Studio 下载太慢的问题的解决办法[详细,来源于学习的笔记] Visual Studio 下载太慢的解决办法两个步骤即可: 一.测试DNS 二.修改host 做完以上工作后,VS的下 ...

  5. vue2学习笔记(二)

    vue学习笔记(二) 组件化编码的流程 组件的自定义事件 全局事件总线(GlobalEventBus) 消息订阅与发布 nextTick Vue封装的过渡与动画 配置代理 github搜索案例 发送a ...

  6. 5天学习MYSQL数据库第一天剩余全部笔记(超级详细的mysql入门笔记适合新手反复看加深记忆)

    这是关于五天学习MYSQL数据库的笔记,如果想要观看视频可以访问(视频链接(b站) 或者访问视频链接 之前的笔记已经记到了1.3的mysql基本介绍,接下来主要是: 2.1MySQL服务端框架 一. ...

  7. 最详细的 K8S 学习笔记总结(2021最新版)

    虽然 Docker 已经很强大了,但是在实际使用上还是有诸多不便,比如集群管理.资源调度.文件管理等等.那么在这样一个百花齐放的容器时代涌现出了很多解决方案,比如 Mesos.Swarm.Kubern ...

  8. 墙裂推荐!看完全面掌握,最详细的 Docker 学习笔记总结(2021最新版)

    Docker 是什么? Docker 是一个开源的容器引擎,可以轻松的为任何应用创建一个轻量级的.可移植的.自给自足的容器.开发者和系统管理员在笔记本上编译测试通过的容器可以批量地在生产环境中部署,包 ...

  9. 禁欲28天!一宅男居然肝出如此详细Web安全学习笔记,学妹看完直接抽搐了!(第二弹)

    2.1. 网络基础 2.1.1. 计算机通信网的组成 计算机网络由通信子网和资源子网组成.其中通信子网负责数据的无差错和有序传递,其处理功能包括差错控制.流量控制.路由选择.网络互连等. 其中资源子网 ...

  10. Vue2学习笔记(尚硅谷张天禹老师)day-01

    Vue 一.vue核心 1.vue简介 1.1.vue是什么 一套用于构建用户界面的渐进式JavaScript框架. 渐进式:vue可以自底向上逐层应用:简单应用:只需一个轻量小巧的核心库:复杂应用: ...

最新文章

  1. 2022-2028年中国白手套行业市场全景调查及发展前景分析报告
  2. SpringMVC的文件上传和拦截器
  3. OpenYurt 开源 | 云原生生态周报 Vol. 51
  4. python 零散记录(三) 格式化字符串 字符串相关方法
  5. boost::fill相关的测试程序
  6. 飞鸽传书文件传输实现原理
  7. 最长上升子序列(信息学奥赛一本通-T1281)
  8. 机器学习与计算机视觉(数据集的选择)
  9. apache配置防盗链
  10. pyglet: a cross-platform windowing and multimedia
  11. 数字图像处理的起源与应用
  12. MATLAB函数拟合使用
  13. 网络工程师(软考)学习笔记3--计算机网络体系结构2
  14. OneNote使用技巧 - 7.一键修改字体样式大小(Onestatic宏插件使用,附常用宏命令代码)
  15. HDU 3065 病毒侵袭持续中 【AC自动机模版题】
  16. 关于Python3爬虫抓取豆瓣电影的案例-利用正则表达式
  17. druid数据源检测数据库连接有效性testOnBorrow、testOnReturn、testWhileIdle属性原理分析
  18. AlphaGo设计师黄士杰:“最强的学习技能在人类的脑袋里”
  19. 超级表格新功能:表格数据支持卡片模式查看
  20. 菱形图案c语言程序,C语言程序设计,做一个菱形图案

热门文章

  1. 报表同比环比sql笔记
  2. Windows徽标键快捷键
  3. CentOS利用WebHook实现PHP自动部署Git代码
  4. 欧美html游戏,欧美HTML社区服务游戏
  5. 使用git上传代码遇到关于remote: Support for password authentication was removed on August 13, 2021.的问题
  6. STM32 f103搭配LM386声音传感器实现简单音乐识别
  7. 苹果手机电池容量已经82%了,需要更换吗?
  8. 教程:Nodejs大漠插件开发游戏脚本实战
  9. 如何选择适合你的兴趣爱好(六),钢琴
  10. java.util.Date中的loe_Java-学习日记(日期的转换与处理)