【Vue】黑马Vue入门到高级实战汇总
目录
- v-text v-cloak
- 计算属性computed
- 计算属性双向绑定
- 监听器watch
- Class与Style绑定v-bind
- 条件渲染v-if
- v-if 与 v-show 比较
- 列表渲染 v-for
- 事件处理 v-on
- 事件修饰符
- 按键修饰符
- 表单数据双向绑定v-model
- 过渡&动画效果
- 过滤效果案例
- 动画效果案例
- 自定义指令
- MVVM设计模式
- 绑定语法
- v-bind
- v-show
- v-if
- v-show 和 v-if 对比
- 观察者模式
- v-for
- 数组中响应式的方法
- key
- v-html
- 绑定HTML片段内容
- 防止用户短暂看到{{}}
- v-cloak
- v-text
- v-on
- 事件绑定
- 事件对象
- vue中如何获得事件对象 (vue中如何获得鼠标位置)
- 参数传递
- $event
- v-once
- v-pre
- 双向绑定
- v-model
- 监控函数 watch
- 绑定样式
- 绑定内联样式
- 绑定class
- 自定义指令
- 计算属性
- 过滤器
- axios
- 组件
- 组件化开发
- 定义子组件
- 组件间传递数据
- SPA: Single Page Application
- VUE脚手架
- 安装生成脚手架代码的工具 vue create
- 运行 npm run serve
- 为脚手架添加axios模块
- 脚手架项目结构
- 脚手架和SPA应用代码的结构
- es6模块化开发在脚手架中的应用
- 用VUE脚手架项目开发
- 组件的生命周期
- 总结
v-text v-cloak
<style>/* 将带有 v-clock 属性的标签隐藏 */[v-clock] {display: none}</style>
</head>
<body><div id="app" v-clock><!-- v-pre1. 用于显示双大括号{{}}2. 跳过这个元素和它的子元素的编译过程,这样可以提高性能。 --><span v-pre>{{您好!}}</span><!-- 使用双大括号,会有双括号 {{}} 闪烁出来。可以通过 v-text进行解决闪烁问题如果我就需要使有双大括号,又不想让它有{{}} 闪烁出来--><h3>{{message}}</h3><h3>{{message}}</h3><h3>{{message}}</h3><h3 v-text="message"></h3><h3 v-text="message"></h3><h3 v-text="message"></h3></div><script src="./node_modules/vue/dist/vue.js"></script><script>new Vue({el: '#app',data: {message: 'hello word.....'}})</script>
</body>
计算属性computed
computed 选项定义计算属性
计算属性 类似于 methods 选项中定义的函数
需求:输入数学与英语分数,采用 methods 与 computed 分别计算出总得分
<body>
<div id="app">数学:<input type="text" v-model="mathScore" >英语:<input type="text" v-model="englishScore">总分(方法-单向):<input type="text" v-model="sumScore()"> 总分(计算属性-单向):<input type="text" v-model="sumScore1">
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script type="text/javascript"> var vm = new Vue({el: '#app',data: {mathScore: 80, englishScore: 90, },methods: { //不要少了s sumScore: function () { //在控制台输入 vm.sumScore() 每次都会被调用console.info('sumScore被调用') // `this` 指向当前 vm 实例, 减 0 是为了字符串转为数字运算return (this.mathScore-0) + (this.englishScore-0) }},computed: { //计算属性 sumScore1 : function () { //在控制台输入vm.sumScore1 不会被重新调用,说明计算属性有缓存console.info('sumScore1被调用') return (this.mathScore - 0) + (this.englishScore -0) } } }) </script>
</body>
computed 选项内的计算属性默认是 getter
函数,所以上面只支持单向绑定,当修改数学和英语的数据才会
更新总分,而修改总分不会更新数据和英语
计算属性双向绑定
计算属性默认只有 getter ,不过在需要时你也可以提供一个 setter
<body>
<div id="app">
数学:<input type="text" v-model="mathScore" ><br>
英语:<input type="text" v-model="englishScore"><br>
总分(方法-单向):<input type="text" v-model="sumScore()"><br>
总分(计算属性-单向):<input type="text" v-model="sumScore1"><br>
总分(计算属性-双向):<input type="text" v-model="sumScore2"><br>
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script type="text/javascript">
var vm = new Vue({ el: '#app',data: {mathScore: 80, englishScore: 90, },methods: { //不要少了s sumScore: function () { //在控制台输入 vm.sumScore() 每次都会被调用console.info('sumScore被调用') // `this` 指向当前 vm 实例, 减 0 是为了字符串转为数字运算 return (this.mathScore-0) + (this.englishScore-0) } },computed: { //计算属性 默认 getter 只支持单向绑定 sumScore1: function(){//在控制台输入vm.sumScore1 不会被重新调用,说明计算属性有缓存console.info('sumScore1被调用') return (this.mathScore - 0) + (this.englishScore -0) },//指定 getter/setter 双向绑定 sumScore2 : { get: function() {console.info('sumScore2被调用') return (this.mathScore-0) + (this.englishScore-0) }, set: function(newValue) {//value为更新后的值 // 被调用则更新了sumScore2,然后将数学和英语更新为平均分var avgScore = newValue / 2 this.mathScore = avgScore this.englishScore = avgScore } } } }) </script>
</body>
监听器watch
当属性数据发生变化时,对应属性的回调函数会自动调用, 在函数内部进行计算
通过 watch 选项 或者 vm 实例的 $watch()
来监听指定的属性
注意: 在data 选择中添加一个 sumScore3 属性
<body><div id="app">数学:<input type="text" v-model="mathScore" ><br> 英语:<input type="text" v-model="englishScore"><br>总分(方法-单向):<input type="text" v-model="sumScore()"><br>总分(计算属性-单向):<input type="text" v-model="sumScore1"><br> 总分(计算属性-双向):<input type="text" v-model="sumScore2"><br> 总分(监听器):<input type="text" v-model="sumScore3"><br> </div><script src="./node_modules/vue/dist/vue.js"></script> <script type="text/javascript"> var vm = new Vue({el: '#app',data: {mathScore: 80,englishScore: 90,sumScore3: 170 },methods: { //不要少了s sumScore: function () { //在控制台输入 vm.sumScore() 每次都会被调用console.log('sumScore被调用')// `this` 指向当前 vm 实例, 减 0 是为了字符串转为数字运算 return (this.mathScore - 0) + (this.englishScore -0) } },// 计算属性computed: { // 默认 getter 只支持单向绑定 sumScore1 : function () { //在控制台输入 vm.sumScore1 不会被重新调用 ,说明计算属性有缓存console.log('sumScore1被调用')return (this.mathScore - 0) + (this.englishScore -0) },//指定 getter/setter 双向绑定 sumScore2 : { get: function () {console.log('sumScore2被调用') return (this.mathScore-0) + (this.englishScore-0) }, set: function (newValue) {//value为更新后的值 // 被调用则更新了sumScore2,然后将数学和英语更新为平均分 var avgScore = newValue / 2 this.mathScore = avgScore this.englishScore = avgScore }} },//监听器方式1:watch选项 watch : { //当数学修改后,更新总分sumScore3mathScore : function (newValue, oldValue) { //newValue 就是新输入的数学得分 this.sumScore3 = (newValue-0) + (this.englishScore-0)} }})//监听器方式2:通过vm对象调用 //第1个参数为监听的属性名,第2个回调函数 vm.$watch('englishScore', function (newValue) { //newValue 就是新输入的英语得分 this.sumScore3 = (newValue-0) + (this.mathScore-0) }) </script>
</body>
Class与Style绑定v-bind
通过 class 列表和 style 指定样式是数据绑定的一个常见需求。它们都是元素的属性,都用 v-bind 处理,其中表达
式结果的类型可以是:字符串、对象或数组。
语法格式
v-bind:class='表达式'
或 :class='表达式'
- 字符串
:class="activeClass"
- 对象
:class="{active: isActive, error: hasError}"
- 数组
:class="['active', 'error']"
注意要加上单引号,不然是获取data中的值
v-bind:style='表达式'
或 :style='表达式'
<body><!-- 第2步:定义样式 --><style>.active {color: green; }.delete {background: red; }.error { font-size: 30px;} </style> <div id="app"> <h2>Class绑定,v-bind:class 或 :class</h2> <!--activeClass会从data中获取值为active,则对应样式为绿色--> <p v-bind:class="activeClass">字符串达式</p> <!-- isDelete为 true,渲染delete样式;当 hasError为false,不取 error 样式;--> <p :class="{delete: isDelete, error: hasError}">对象表达式</p> <!--- 渲染 'active', 'error' 样式,注意要加上单引号,不然是获取data中的值 --> <p :class="['active', 'error']">数组表达式</p> <h2>Style绑定, v-bind:style 或 :class</h2> <p :style="{color: activeColor, fontSize: fontSize + 'px'}">Style绑定</p> </div> <script src="./node_modules/vue/dist/vue.js"></script> <script type="text/javascript"> new Vue({el: '#app',data: { activeClass: 'active',isDelete: true,hasError: false,//演示 Style绑定 activeColor: 'red', fontSize: 20 } })</script>
</body>
条件渲染v-if
<body><style>.box {width: 200px; height: 200px;background: red;}</style><div id="app"><h2>v-if 条件渲染</h2> <input v-model="seen" type="checkbox" >勾选后显示红色小块 <!-- v-if 为 true则显示渲染当前元素, --><div v-if="seen" class="box" ></div> <p v-else="seen">红块已隐藏</p> <h2>v-show 条件渲染</h2> <!-- v-show 的元素始终会被渲染并保留在 DOM 中,只是简单切换元素的 CSS 属性 display 显示隐藏,而不是重新加载div--> <div v-show="seen" class="box" ></div> </div> <script src="./node_modules/vue/dist/vue.js"></script> <script type="text/javascript">var vm = new Vue({el: '#app',data: {seen: false} }) </script>
</body>
v-if 与 v-show 比较
什么时候元素被渲染
v-if 如果在初始条件为假,则什么也不做,每当条件为真时,都会重新渲染条件元素
v-show 不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换
使用场景选择
v-if 有更高的切换开销,
v-show 有更高的初始渲染开销。
因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行后条件很少改变,则使用 v-if 较好。
列表渲染 v-for
列表渲染指令
v-for 迭代数组
语法: v-for="(alias, index) in array"
说明: alias
: 数组元素迭代的别名; index
: 数组索引值从0开始(可选) 。
<div v-for="item in items" :key="item.id"></div>
<div v-for="(item, index) in items" :key="item.id"></div>
items
是源数据数组, item
是数组元素迭代的别名。
注意:使用 key
特殊属性, 它会基于 key 的变化重新排列元素顺序,并且会移除 key 不存在的元素
<div v-for="value in object" ></div>
<div v-for="(value, key) in object"></div>
<div v-for="(value, key, index) in object"></div>
注意: 在遍历对象时,是按 Object.keys()
的结果遍历,但不能保证它的结果在不同的 JavaScript 引擎下是顺序一致的。
<body><div id="app"><h2>1. 迭代数组</h2><ul><!-- e 为当前对象别名,index 数组下标0开始--><li v-for="(e, index) in emps" :key="index">编号:{{index+1}},姓名:{{e.name}},工资:{{e.salary}} </li></ul> <br> <h2>2. 迭代对象</h2> <ul><!-- value是属性值,key是属性名,index索引值--> <li v-for="(value, key, index) in emps[0]">第{{index+1}}个属性为:{{key}} = {{value}}</li></ul> </div><script src="./node_modules/vue/dist/vue.js"></script> <script type="text/javascript">var vm = new Vue({el: '#app',data: {emps:[ //数组{name: '马云', salary: '20000'}, {name: '马化腾', salary: '18000'}, {name: '刘强东', salary: '13000'}] } }) </script>
</body>
事件处理 v-on
事件处理方法
完整格式: v-on:事件名="函数名"
或 v-on:事件名="函数名(参数……)"
缩写格式: @事件名="函数名"
或 @事件名="函数名(参数……)"
注意: @ 后面没有冒号
event
:函数中的默认形参,代表原生 DOM 事件
当调用的函数,有多个参数传入时,需要使用原生DOM事件,则通过 $event
作为实参传入
作用:用于监听 DOM 事件
<body><div id="app"><h2>1. 事件处理方法</h2> <button @click="say">Say {{msg}}</button> <button @click="warn('hello', $event)">Warn</button></div> <script src="./node_modules/vue/dist/vue.js"></script> <script type="text/javascript">var vm = new Vue({el: '#app',data: {msg: 'Hello, Vue.js'},methods: { say: function (event) {// `this` 在方法里指向当前 Vue 实例 alert(this.msg) // `event` 是原生 DOM 事件 alert(event.target.innerHTML)},//多个参数如果需要使用原生事件,将 $event 作为实参传入 warn: function (msg, event) { alert(msg + "," + event.target.tagName) } } })</script>
</body>
事件修饰符
.stop
阻止单击事件继续传播 event.stopPropagation()
.prevent
阻止事件默认行为 event.preventDefault()
.once
点击事件将只会触发一次
<body><div id="app"><h2>1. 事件处理方法</h2><button @click="say">Say {{msg}}</button><button @click="warn('hello', $event)">Warn</button> <br><h2>2. 事件修饰符</h2> <!--单击事件继续传播--> <div @click="todo"> <!--点击后会调用doThis再调用todo--> <button @click="doThis">单击事件会继续传播</button> </div> <br/> <!-- 阻止单击事件继续传播,--> <div @click="todo"> <!--点击后只调用doThis--> <button @click.stop="doThis">阻止单击事件会继续传播</button> </div> <!-- 阻止事件默认行为 --> <a href="http://www.baidu.com" @click.prevent="doStop">百度</a><!-- 点击事件将只会触发一次 --> <button @click.once="doOnly">点击事件将只会触发一次: {{num}}</button>
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script type="text/javascript"> var vm = new Vue({el: '#app',data: {msg: 'Hello, Vue.js',num: 1 },methods: {say: function (event) {// `this` 在方法里指向当前 Vue 实例 alert(this.msg) // `event` 是原生 DOM 事件 alert(event.target.innerHTML) },//多个参数如果需要使用原生事件,将 $event 作为实参传入 warn: function (msg, event) {alert(msg + "," + event.target.tagName)},todo: function () {alert("todo...."); },doThis: function () {alert("doThis....");},doStop: function () {alert("href默认跳转被阻止....")},doOnly: function() {this.num++ } } }) </script>
</body>
按键修饰符
<body><div id="app"><h3>1. 事件处理方法 v-on 或 @</h3><button v-on:click="say">Say {{msg}}</button><!-- $event代表的是原生 的Dom事件 --><button @click="warn('hello', $event)">Warn</button><h3>2. 事件修饰符</h3><!-- 2.1 防止单击事件继续传播 --><div @click="todo"><button @click="doThis">单击事件会继续传播</button></div><br><div @click="todo"><!-- .stop作用:是阻止事件的传播 --><!--点击后只调用doThis--><button @click.stop="doThis">阻止单击事件会继续传播</button></div><br><!-- 2.2 阻止事件的默认行为 --><a href="https://cn.vuejs.org/" @click.prevent="doStop">vue官方文档</a><!-- 2.3 点击事件只会触发一次 --><button @click.once="doOnly">点击事件只会触发一次:{{num}}</button><br><h3>3. 按键修饰符或按键码</h3><input type="text" @keyup.enter="keyEnter"><!--进入输入框按回车时调用keyEnter--><input type="text" @keyup.space="keySpace"><!--进入输入框按回车时调用keySpace--><input type="text" @keyup.13="keyCode"></div><script src="./node_modules/vue/dist/vue.js"></script><script>new Vue({el: '#app',data: {msg: 'Hello word!!',num: 0},methods: { //定义事件处理函数say: function (event) {// event代表的是Dom原生事件, Vue.js它会自动 的将它传入进来,// `this` 在方法里指向当前 Vue 实例alert(this.msg)alert(event.target.innerHTML)},warn: function (name, event) {//如果说函数有多个参数,而双需要使用原生事件,则需要使用 $event 作为 参数传入alert(name + ',' + event.target.tagName)},doThis: function () {alert('doThis....')},todo: function () {alert('todo....')},doStop: function () {alert('doStop...href默认行为已经被阻止')},doOnly: function () {this.num ++ },keyEnter: function () {alert('当前按的是回车键')},keySpace: function() {alert('当前按的是空格键')},keyCode: function () {alert('按的是13')}},})</script>
</body>
表单数据双向绑定v-model
基础用法
v-model 指令用于表单数据双向绑定,针对以下类型:
text 文本
textarea 多行文本
radio 单选按钮
checkbox 复选框
select 下拉框
<body><div id="demo"><!-- @submit.prevent 阻止事件的默认行为,当前阻止的是action行为 --><form action="#" @submit.prevent="submitForm">姓名(文本):<input type="text" v-model="name"><br><br>性别(单选按钮):<input name="sex" type="radio" value="1" v-model="sex"/>男<input name="sex" type="radio" value="0" v-model="sex"/>女<br><br>技能(多选框):<input type="checkbox" name="skills" value="java" v-model="skills">Java开发<input type="checkbox" name="skills" value="vue" v-model="skills">Vue.js开发<input type="checkbox" name="skills" value="python" v-model="skills">Python开发<br><br>城市(下拉框):<select name="citys" v-model="city"><option v-for="c in citys" :value="c.code">{{c.name}}</option></select><br><br>说明(多行文本):<textarea cols="30" rows="5" v-model="desc"></textarea><br><br><button type="submit" >提交</button></form>
</div><script src="./node_modules/vue/dist/vue.js"></script>
<script>new Vue({el: '#demo',data: {name: '',sex: '1', //默认选中的是 男skills: ['vue', 'python'], //默认选中 Vue.js开发 、Python开发citys: [//初始化下拉框{code: 'bj', name: '北京'},{code: 'sh', name: '上海'},{code: 'gz', name: '广州'}],city: 'sh', // 默认选中的城市:上海desc: ''},methods: {submitForm: function () { //处理提交表单//发送ajax异步处理alert(this.name + ', ' + this.sex + ', ' + this.skills + ', ' + this.city + ', ' + this.desc)}}})</script>
</body>
过渡&动画效果
什么是过渡&动画
元素在显示和隐藏时,实现过滤或者动画的效果。
过渡与动画时,会为对应元素动态添加的相关 class 类名:
- xxx-enter :定义显示前的效果。
- xxx-enter-active :定义显示过程的效果。
- xxx-enter-to : 定义显示后的效果。
- xxx-leave : 定义隐藏前的效果。
- xxx-leave-active :定义隐藏过程的效果。
- xxx-leave-to :定义隐藏后的效果。
过滤效果案例
1.为目标元素添加父元素 <transition name="xxx">
2.定义 class 过渡样式
3.功能实现
点击按钮后, 显示隐藏文本
效果1:显示和隐藏有渐变效果
效果2:显示和隐藏的有平移效果,并持续时长不同
<style>/* 显示或隐藏的过渡效果 */.mxg-enter-active, .mxg-leave-active {transition: opacity 1s; /*过渡:渐变效果持续时长1秒 */}/* 显示前或隐藏后的效果 */.mxg-enter, .mxg-leave-to {opacity: 0; /* 都是隐藏效果 */}/* 可以针对显示和隐藏指定不同的效果 *//* 显示过渡效果 1秒 */.meng-enter-active {transition: all 1s; /*all 所有效果,持续1秒*/}/* 隐藏过渡效果 5秒 */.meng-leave-active {transition: all 5s; /*all 所有效果,持续5秒*/}/* 显示前或隐藏后的效果 */.meng-enter, .meng-leave-to {opacity: 0; /* 都是隐藏效果 */transform: translateX(10px); /*水平方向 移动 10px*/}</style>
</head>
<body><div id="app1"><button @click="show = !show">渐变过渡</button><transition name="mxg"><p v-show="show" >mengxuegu</p></transition></div><div id="app2"><button @click="show = !show">渐变平滑过渡</button><transition name="meng"><p v-show="show" >vue</p></transition></div><script src="./node_modules/vue/dist/vue.js"></script><script>new Vue({el: '#app1',data: {show: true}})new Vue({el: '#app2',data: {show: true}})</script>
</body>
动画效果案例
CSS 动画用法同 CSS 过渡,只不过采用 animation 为指定动画效果
功能实现:
点击按钮后, 文本内容有放大缩小效果
注意:官网上面源码有问题,要在 <p>
元素上增加样式 style="display: inline-block"
<style>/* 显示过程中的动画效果 */.bounce-enter-active {animation: bounce-in 1s;}/* 隐藏过程中的动画效果 */.bounce-leave-active {animation: bounce-in 1s reverse;}@keyframes bounce-in {0% { /*持续时长百分比,比如针对1s: 0%代表0秒,50%代表0.5*/transform: scale(0); /*缩小为0*/}50% {transform: scale(1.5); /*放大1.5倍*/}100% {transform: scale(1); /*原始大小*/}}</style>
</head><body><div id="demo"><button @click="show = !show">放大缩小动画</button><transition name="bounce"><p v-show="show" >vuejs</p></transition></div><script src="./node_modules/vue/dist/vue.js"></script><script>new Vue({el: '#demo',data: {show: true}})</script>
</body>
自定义指令
// 指令名不要带 v-
Vue.directive('指令名', {// el 代表使用了此指令的那个 DOM 元素 // binding 可获取使用了此指令的绑定值 等 inserted: function (el, binding) {// 逻辑代码 }
})
directives : {'指令名' : { // 指令名不要带 v-inserted (el, binding) {// 逻辑代码 } }
}
3.使用指令:
引用指令时,指令名前面加上 v-
直接在元素上在使用即可 : v-指令名='表达式'
<body>
<div id="app"><p v-upper-text="message">xxxxx</p>自动获取焦点:<input type="text" v-focus>
</div>
<div id="app2"><p v-upper-text="msg">xxxxx</p></div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script>// 注册全局自定义指令,可以在多个Vue管理的入口下使用该指令// 第一个参数为指令名,但是不要有v-开头// 注册一个全局 v-upper-text 指令Vue.directive('upper-text',{//一般对样式 的操作在bind中,bind函数只调用一次 //因为是样式,所以不需要元素插入到DOM中,就好像link引入CSS文件时并不关心元素是否加载bind: function (el) {el.style.color = 'red'},//一般对js操作在inserted中,inserted也是只调用一次// el 代表使用了此指令的那个 DOM 元素// binding用于获取使用了当前指令的绑定值(value)、表达式(expression)、指令名(name)等inserted: function (el, binding) {// 将在 v-upper-text 指令中获取到的值,变成大写输出到标签体中el.innerHTML = binding.value.toUpperCase()}})new Vue({el: '#app',data: {message: '渐进式JavaScript 框架 '},//注册局部自定义指令:只能在当前Vue实例管理的入口 下引用这个指令directives: {//注册一个局部指令 v-focus'focus': { // 指令名,bind: function () {},//和js行为有关的操作,最好在inserted中执行,和样式相关的操作都可在bind中执行// 刷新页面自动获取焦点inserted: function (el, binding) {//被 v-focus 作用的那个元素在刷新页面后会自动 获取焦点el.focus()}}}})
</script>
</body>
MVVM设计模式
- 什么是: 对前端三大部分代码的重新划分
- 旧划分:
(1). HTML 专门定义网页内容的语言
(2). CSS专门定义网页样式的的语言
(3). Js专门操作页面内容和样式,添加交互行为的语言 - 问题: 因为HTML和CSS很弱!缺少编程语言必须的要素!
(1). 没有变量,如果想让内容虽程序自动改变,不可能!
(2). 缺少必要的程序结构: 没有分支和循环
导致: JS要承担一切操作页面的代码!导致JS代码繁琐,且重复代码极多! - 新划分:
(1). 界面/视图(View):
a. 包括传统的HTML+CSS
b. 增强了HTML的功能!
1). 比如: HTML中可以写变量!
2). HTML中可以写if else if else 也可以写for循环
3). HTML中可以写事件绑定!
(2). 模型数据(Model): 页面上所有需要的变量,集中保存在一个对象中!
问题: 模型数据中的变量值,不会自动跑到页面上指定位置的!
(3). 视图模型(ViewModel): 用一个对象将视图(View)和模型对象(Model)绑定起来!
绑定结果: 数据模型中的变量值,可以自动跑到视图中指定位置,无需任何js编码!且模型对象中数据改变,视图中对应位置的变量值跟着自动变化! - MVVM的原理: Vue框架是如何实现MVVM设计模式的
(1). new Vue()加载data对象
a. 将data对象打散,data内部的属性直接隶属于new Vue()对象
b. 将data中每个原始属性隐姓埋名,隐藏
c. 为data中每个属性请保镖:
1). Data中每个属性都有一对儿get/set方法
2). 今后只要想修改data中的变量都会自动触发set()
3). 在每个属性的set方法中,都自动植入一个notify()函数调用,只要试图修改data中的属性值时,都会自动调用set(),只要自动调用set()势必会自动notify()发出通知
(2). 加载虚拟DOM树:
a. 通过el属性值的选择器找到要监控区域的父元素
b. 创建虚拟DOM树
c. 扫描这个要监控的区域:
1). 每发现一个{{变量}}的元素,就将该元素的信息,记录进虚拟DOM树,同时首次用data中同名变量的值,代替页面中{{n}}的位置。
2). 每发现一个@事件名="函数名"的元素,就自动变为:
On事件名=“new Vue().函数名”
(3). 加载methods对象: methods对象中的所有方法,都会被打散,直接隶属于new Vue()和data中被打散的属性平级
所以,在methods中的方法中,想操作data中的属性,都可以写为"this.属性名"即可!
(4). 当触发事件时,自动调用new Vue()中methods中指定的函数,执行其中this.属性名的修改。修改会自动触发属性的set()方法,自动触发set()内部的notify函数:
a. 遍历虚拟DOM树,只找出受影响的个别元素
b. 利用虚拟DOM树提前封装好的DOM操作,只修改页面中受影响的个别元素!——效率高! - 总结: MVVM的原理/Vue的绑定原理:
访问器属性+观察者模式+虚拟DOM树 - 什么是虚拟DOM树:
(1). 什么是虚拟DOM树: 仅保存可能发生变化的少量元素的精简版DOM树
(2). 优点:
a. 小, 遍历快!
b. 只更新受影响的元素,效率高!
c. 封装了DOM操作,无需我们程序员重复编码!
绑定语法
- 什么是绑定语法: 让HTML中的内容也可随程序中的变量改变而自动改变!——也就是给HTML中添加变量功能
- 何时: 只要元素的内容中想随变量自动改变
- 如何:
<元素>xxxx{{变量名}}xxxx</元素>
- 原理:
(1). 首次加载页面内容时,会用data中同名变量的初始值代替{{变量名}}位置
(2). 当data中同名变量在new Vue()中被更改时,自动更新{{变量名}}位置为新值 - 总结: {{}}中可以放什么,不能放什么
(1). 可以放: 变量,表达式,函数调用,创建对象,访问数组元素,三目
(2). 不能放: 程序结构(分支和循环),没有返回值的函数调用 - 示例: 使用vue在页面上显示不同内容
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="js/vue.js"></script>
</head>
<body>
<div id="app"><h1>用户名:{{uname}}</h1><h2>性别:{{sex==1?"男":"女"}}</h2><h3>下单时间:{{new Date(orderTime).toLocaleString()}}</h3><h3>小计:¥{{(price*count).toFixed(2)}}</h3>
</div>
<script>
var vm=new Vue({el:"#app",data:{uname:"dingding",sex:1,orderTime:1579507293225,price:12.5,count:5//将来这些值都来自于ajax请求}
})
</script>
</body>
v-bind
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title><script src="js/vue.js"></script>
</head>
<body>
<div id="app">
<!--希望img的src属性,随变量PM25的值变化如果pm25<100, src变为1.png否则如果pm25<200, src变为2.png否则如果pm25<300, src变为3.png否则src变为4.png-->
<img :src="pm25<100?'img/1.png':pm25<200?'img/2.png':pm25<300?'img/3.png':'img/4.png'" >
<h1>{{pm25<100?'img/1.png':pm25<200?'img/2.png':pm25<300?'img/3.png':'img/4.png'}}</h1>
</div>
<script>
var vm=new Vue({el:"#app",data:{//本例中: 因为页面上只需要一个变量pm25,所以data中只有一个变量pm25pm25:125}
})
setInterval(function(){vm.pm25=Math.random()*400
},1000)
</script>
</body>
<div id="app"><img v-bind:src="imgURL" alt="">
</div><script>var vm=new Vue({el:"#app",data:{imgURL:"https://cn.vuejs.org/images/logo.png" +"?_sw-precache=cf23526f451784ff137f161b8fe" +"18d5a"}})</script>
<style>.active{color: red;}</style>
</head>
<body>
<div id="app"><!--<h2 class="active">{{message}}</h2>--><!--<h2 :class="active">{{message}}</h2>--><!--对象写法 键是类名 值是布尔值 通过修改布尔值选择类名,会与内部class合并不会覆盖--><h2 v-bind:class="{active: isActive , line: isLine}">{{message}}</h2><h2 v-bind:class="getClasses()">{{message}}</h2><button v-on:click="btnClick()" >点一下变色</button>
</div><script>var vm=new Vue({el:"#app",data:{message:"你好啊",isActive:true,isLine:true},methods:{btnClick:function(){this.isActive = !this.isActive},getClasses: function(){return {active: this.isActive , line: this.isLine}}}})</script>
<div id="app">
<!--<h2 :style="{key(属性名): value(属性值)}">{{message}}</h2>-->
<!--style后跟一个对象类型, 对象的key是css属性名称,对象的value是具体赋的值,值可以来自于data中的属性--><!--50px必须带引号 vue中属性值不带引号会被当作变量解析--><!--<h2 :style="{fontSize: '50px'}">{{message}}</h2>--><!--finalSize当成一个变量使用--><!--<h2 :style="{fontSize: finalSize}">{{message}}</h2>--><!--<h2 :style="{fontSize: finalSize + 'px', color: finaColor}">{{message}}</h2>--><h2 :style="getStyles()">{{message}}</h2></div>
<script src="vue.js"></script>
<script>const app = new Vue({el:'#app',data:{message:'你好啊',// finalSize: '100px'finalSize: 100,finaColor:'red'},methods: {getStyles: function(){return{fontSize: this.finalSize + 'px', color: this.finaColor}}}})
</script>
<div id="app"><!--数组内不带引号表示变量,带了变字符串--><h2 class="title" :class="[active, line]">{{message}}</h2><h2 class="title" :class="getClasses()">{{message}}</h2>
</div><script>var vm=new Vue({el:"#app",data:{message:"你好啊",// 服务器请求过来的变量aaa bbbactive:'aaa',line:'bbb'},methods: {getClasses: function () {return [this.active, this.line]}}})</script>
<div id="app"><!--style后跟一个数组类型, 多个值以逗号分割--><h2 :style="[baseStyle,baseStyle1]">{{message}}</h2>
</div>
<script src="vue.js"></script>
<script>const app = new Vue({el:'#app',data:{message:'你好啊',baseStyle: {backgroundColor: 'red'},baseStyle1:{fontSize: '100px'},}})
</script>
v-show
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title><style>.alert{width:300px;height:100px;position:fixed;padding:10px;top:50%;left:50%;margin-left:-160px;margin-top:-60px;background-color:#faf}.alert>span{cursor:pointer;border:1px solid #fff;float:right;padding:5px;}</style><script src="js/vue.js"></script>
</head>
<body>
<div id="app"><!--希望单击按钮时,让对话框显示--><button @click="showIt">click me</button><div v-show="show" class="alert"><!--希望点x时,关闭对话框--><span @click="close">x</span>您的浏览器版本太低,请升级!</div>
</div>
<script>
var vm=new Vue({el:"#app",data:{//本例中: 因为页面上需要一个bool类型的变量show,控制对话框的显示隐藏show:false},methods:{//因为页面上单击按钮需要调用名为showIt的函数,所以//showIt:function(){showIt(){//本例中: 调用showIt为了让对话框显示!//所以: this.show=true;},//因为页面上单击x按钮需要调用名为close的函数,所以close(){//本例中: 调用close为了关闭对话框this.show=false;}}
})
</script>
</body>
v-if
a. 也能控制一个元素显示隐藏,上例中v-show可直接换成v-if。
b. 但是原理不同:v-if在扫描时,如果条件为true,则保留该元素。否则如果条件为false,则删除该元素!
v-show 和 v-if 对比
<div id="app">
<!--希望如果已经登录,显示第一个div如果点注销,希望状态改为未登录-->
<div v-if="isLogin" id="div1">Welcome dingding | <a href="javascript:;" @click="logout">注销</a>
</div>
<!--否则如果未登录时,显示第二个div如果点登录,则状态改为已登录-->
<div v-else id="div2"><a href="javascript:;" @click="login">登录</a> | <a href="javascript:;">注册</a>
</div><script>
var vm=new Vue({el:"#app",data:{//因为界面上需要一个变量isLogin来表示是否登录isLogin:false},methods:{//因为页面上单击登录,需要调用login函数login(){//点击登录,修改状态为已登录this.isLogin=true;},//因为页面上单击注销,需要调用logout函数logout(){//点击注销,修改状态为未登录this.isLogin=false;}}
})
</script>
<div id="app">
<!--随变量PM25的值变化,在四个img中选其一显示,删除其他img。如果pm25<100, 显示1.png否则如果pm25<200, 显示2.png否则如果pm25<300, 显示3.png否则显示4.png-->
<img v-if="pm25<100" src="img/1.png">
<img v-else-if="pm25<200" src="img/2.png">
<img v-else-if="pm25<300" src="img/3.png">
<img v-else src="img/4.png">
</div>
<script>
var vm=new Vue({el:"#app",data:{//本例中: 因为页面上只需要一个变量pm25,所以data中只有一个变量pm25pm25:125}
})
setInterval(function(){vm.pm25=Math.random()*400
},1000)
</script>
<div id="app"><span v-if="isUser"><label for="username">用户账户</label><!--key作为一个标识 不一样就不会复用--><input type="text" id="username" placeholder="用户账户" key="username"></span><span v-else><label for="email">用户邮箱</label><input type="text" id="email" placeholder="用户邮箱" key="email"></span><button @click="isUser = !isUser">切换类型</button>
</div>
<script src="vue.js"></script>
<script>const app = new Vue({el:'#app',data:{isUser:true}})
</script>
如果在有输入内容的情况下,切换了类型,会发现文字依然显示之前的输入内容
这是因为Vue在进行DOM渲染时,处于性能考虑,会尽可能的复用已经存在的元素,而不是重新创建新的元素
Vue内部会发现原来input元素不再使用,直接作为else中的input来使用了
解决方案 :不需要Vue出现类似重复利用的问题 可以给对应的input添加key 取不同值
高频笔试题(手写) 观察者模式
//观察者(observer)模式: 变量值修改,所有关注的人都能自动得到通知,并收到新值
//保存数据的对象
var data={n:0, //外人想随时获得的变量,保存着外人关心的一个数据nobservers:[],//用一个数组保存将来关心这个变量n的所有外人,这些觊觎这个变量n的外人,也称为观察者//定义一个函数,专门修改变量n的值setN(n) {this.n = n//任何时候修改了变量n的值,都可以通知所有关注n的外人this.notifyAllObservers()},//定义一个函数,专门将关注变量n的外人(观察者)对象,集中保存在data中的observers数组中,便于集中通知。addObs(observer) {this.observers.push(observer)},//定义一个函数,专门用于通知当前data中的observers数组中保存的关心变量n的外人们,n发生改变了notifyAllObservers() {//遍历observers数组中每个外人对象this.observers.forEach(observer => {//每遍历一个外人对象,就通知外人,它关心的变量n发生改变了,请及时获取变量n的新值observer.getNewN()})}
}//向data的observers数组中添加三个关心变量n的外人(观察者)对象
for(var i=0;i<3;i++){data.addObs({//每个外人对象都包括两个属性和一个函数name:`obs${i}`, //外人的名字look: data, //每个外人都关心data对象中的变量,都紧紧盯着data对象//每个外人都准备一个函数,当data中变量改变时,可用于重新获得data对象中n的新值。getNewN:function(){console.log(`${this.name} known that n is updated to ${this.look.n}`)}})
}// 测试代码
console.log("data将n改为1时:")
data.setN(1)
console.log("data将n改为2时:")
data.setN(2)
console.log("data将n改为3时:")
data.setN(3)
v-for
<div id="app"><ul><!--遍历不需要索引--><!--<li v-for="item in names">{{item}}</li>--><li v-for="(item, index) in names"><!--下标从0开始 需求从1开始-->{{index+1}}.{{item}}</li></ul>
</div>
<script src="vue.js"></script>
<script>const app = new Vue({el:'#app',data:{names:['why','kobe','james','curry']}})
</script>
<!--v-for="(值,属性,序号) in 对象 -->
<div id="app"><!--1.在遍历对象的过程中,如果只获取一个值,那么获取到的是value--><ul><li v-for="item in info">{{item}}</li></ul><!--2.获取key和value 格式:(value ,key)--><ul><!--括号和in之间没空格就会报错--><!--<li v-for = "(value,key)in info">{{key}}:{{value}}</li>--><li v-for = "(value,key) in info">{{key}}:{{value}}</li></ul><!--3.获取key和value和index 格式:(value ,key)--><ul><li v-for="(value,key,index) in info">{{index}}.{{key}}:{{value}}</li></ul>
</div>
<script src="vue.js"></script>
<script>const app = new Vue({el:'#app',data:{info:{name:'ws',age:18,height:1.88}}})
</script>
<div id="app"><ul><!--本例中: 因为要反复生成多个li,所以v-for要写在li上,而不是li的父元素ul上--><li v-for="(value,i) of teachers" :key ="i">第{{i+1}}阶段: {{value}}</li></ul>
</div>
<script>
var vm=new Vue({el:"#app",data:{teachers:["亮亮","然然","东东","涛涛"]}
})
</script>
<div id="app"><ul><li v-for="(m, index) in movies">{{index}}.{{m}}</li></ul>
</div>
<script src="vue.js"></script>
<script>const app = new Vue({el:'#app',data:{movies:['海贼王','火影忍者','名侦探柯南']}})
</script>
(4)v-for还可:
a. 可遍历数字下标的一切: 比如字符串
b. 可遍历对象中每个属性
示例: 遍历对象中每个属性,反复生成多个html元素
<div id="app"><!--希望遍历data中一个对象的每个属性,反复生成多个相同结构的HTML元素--><ul><li v-for="(value,key) of ym" :key ="key">{{key}} : {{value}}</li></ul>
</div>
<script>
var vm=new Vue({el:"#app",data:{ym:{math:89,chs:69,eng:91}}
})
</script>
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title><style>ul{ list-style:none }ul>li{float:left; border:1px solid #555;width:36px;height:36px;line-height:36px;text-align:center;}ul>li+li{border-left:0}</style><script src="js/vue.js"></script>
</head>
<body>
<div id="app"><ul><li v-for="i of pageCount">{{i}}</li></ul>
</div>
<script>
var vm=new Vue({el:"#app",data:{pageCount:3}
})
</script>
</body>
<style>.active{color: gold;}
</style>
</head>
<body>
<div id="app"><ul><li v-for="(item,index) in movies":class="{active: currentIndex===index}"@click="liClick(index)">{{index}}.{{item}}</li><!--<li :class="{active: 0===currentIndex}"></li>--><!--<li :class="{active: 1===currentIndex}"></li>--><!--<li :class="{active: 2===currentIndex}"></li>--><!--<li :class="{active: 3===currentIndex}"></li>--></ul>
</div>
<script src="vue.js"></script>
<script>const app = new Vue({el:'#app',data:{movies:['唐山大兄','精武门','猛龙过江','龙争虎斗','死亡游戏'],currentIndex:0},methods:{liClick(index){this.currentIndex = index}}})
</script>
购物车完整案例(加减移除总数)
<!doctype html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport"content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title><style>table{border: 1px solid #e9e9e9;border-collapse: collapse;border-spacing: 0;margin: 0 auto;}th,td{padding: 10px 90px;border: 1px solid #e9e9e9;text-align: left;}th{background-color: #f7f7f7;color:#5c6b77;font-weight: 600;}</style>
</head>
<body>
<div id="app"><div v-if="books.length"><table><thead><tr><th></th><th>书籍名称</th><th>出版日期</th><th>价格</th><th>购买数量</th><th>操作</th></tr></thead><tbody><tr v-for="(item,index) in books"><!--<td v-for="value in item">{{value}}</td>--><td>{{item.id}}</td><td>{{item.name}}</td><td>{{item.data}}</td><!--<td>{{item.price.toFixed(2)}}</td>--><!--<td>{{getFinalPrice(item.price)}}</td>--><td>{{item.price|showPrice}}</td><td><button @click="decrement(index)" v-bind:disabled="item.count<=0">-</button>{{item.count}}<button @click="increment(index)">+</button></td><td><button @click="removeHandle(index)">移除</button></td></tr></tbody></table><h2>总价格:{{totalPrice|showPrice}}</h2></div><h2 v-else>购物车为空</h2>
</div>
<script src="vue.js"></script>
<script>const app =new Vue({el:"#app",data:{books:[{id:1,name:'JavaScript权威指南',data:'2021-3',price:139.00,count:0},{id:2,name:'JavaScript高级程序设计',data:'2019-10',price:128.00,count:0},{id:3,name:'你不知道的JavaScript',data:'2021-6',price:204.00,count:0},{id:4,name:'JavaScript设计模式',data:'2019-9',price:59.00,count:0},]},methods:{// getFinalPrice(price){// return "¥"+ price.toFixed(2)// }increment(index){this.books[index].count++},decrement(index){this.books[index].count--},removeHandle(index){this.books.splice(index,1)}},computed:{totalPrice(){// 1.普通for循环// let totalPrice = 0// for(let i=0;i<this.books.length; i++){// totalPrice += this.books[i].price * this.books[i].count// }// return totalPrice// 2.for(let i in this.books)// let totalPrice = 0// for(let i in this.books){// const book = this.books[i]// totalPrice += book.price * book.count// }// return totalPrice//3.for(let i of this.books)// let totalPrice = 0// for(let item of this.books){// totalPrice += item.price * item.count// }// return totalPrice//4.reducereturn this.books.reduce(function(preValue,book){return preValue + book.price * book.count},0)}},filters:{showPrice(price){return "¥"+ price.toFixed(2)}}})
</script>
</body>
</html>
数组中响应式的方法
const app = new Vue({el:'#app',data:{letters:['a','b','c','d','e']},methods:{btnClick(){//0.通过索引值修改数组中的元素(不是响应式)// this.letters[0] = 'b'//页面不会修改 没响应 可以改为splice写法// this.letters.splice(0,1,'b')//set(要修改的对象,索引值,修改后的值)Vue.set(this.letters,0,'b')// 七个响应式数组方法//1.push方法// this.letters.push('A')// 2.pop() : 删除数组中的最后一个元素// this.letters.pop()// 3.shift() : 删除数组中的第一个元素// this.letters.shift()// 4.unshift() : 在数组最前面添加元素// this.letters.unshift('A');// 也可以添加多个元素 vue源码中unshift(...items:T[]):number;有可变参数...items// this.letters.unshift('A','B','C')// 5.splice(start)// splice作用:删除元素/插入元素/替换元素// 删除元素:第二个参数传入需要删除几个元素(如果没有穿,就删除后面所有的元素)// const start = 2;// this.letters.splice(start,this.letters.length-start)// 替换元素:第二个参数表示要替换几个元素,后面是用于替换前面的元素// this.letters.splice(1,3,'f','g','h','j')// 插入元素:第二个参数传入0,并且后面要跟上要插入的元素// this.letters.splice(1,0,'f','g','h')// 6.sort():排序// this.letters.sort()// 7.reverse():反转// this.letters.reverse()}}})
key
官方推荐我们在使用 v-for时,给对应的元素或组件添加上一个:key属性, 这个其实和Vue的虚拟DOM的Diff算法有关系
<div id="app"><ul><li v-for="item in letters" :key="item">{{item}}</li></ul>
</div>
<script src="vue.js"></script>
<script>const app = new Vue({el:'#app',data:{letters:['A','B','C','D','E']}})
</script>
所以我们需要使用key来给每个节点做一个唯一标识
Diff算法就可以正确的识别此节点
找到正确的位置区插入新的节点
v-html
绑定HTML片段内容
<div id="app"><h1>消息来源:{{html}}</h1><h1 v-html="'消息来源:'+html">Welcome</h1>
</div>
<script>
var vm=new Vue({el:"#app",data:{html:`<p>来自<a href="javascript:;"><<新华社>></a>的消息</p>`}
})
</script>
防止用户短暂看到{{}}
问题: 因为vue代码是放在js文件中,所以,如果网速慢,vue代码暂时没有下载下来时,用户很可能短暂看到页面上的绑定语法,用户体验不好!
解决: 2个办法:
v-cloak
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title><style>[v-cloak]{display:none}</style><script src="js/vue.js"></script>
</head>
<body>
<div id="app"><h1 v-cloak>Welcome: {{uname}}</h1>
</div>
<script>
setTimeout(function(){var vm=new Vue({el:"#app",data:{uname:"dingding"}})
},2000)
</script>
</body>
d. 问题: 既要在HTML中写指令,又要手动添加css选择器,步骤繁琐的!
v-text
<div id="app"><h1 v-text="'Welcome:'+uname"></h1>
</div>
<script>
setTimeout(function(){var vm=new Vue({el:"#app",data:{uname:"dingding"}})
},2000)
</script>
v-on
事件绑定
<div id="app"><button @click="change(-1)">-</button><span>{{n}}</span><button @click="change(+1)">+</button>
</div>
<script>
var data={ n:0 }
var vm=new Vue({el:"#app",data:data,methods:{//本例中,因为页面上只需要一个change函数,所以methods中只添加一个change函数//但是页面上调用change()函数时,传入了一个参数值,所以定义change函数时需要定义一个形参来接住实参值change(i){//如果i==+1,说明本次想+1if(i==+1){this.n++;}else{//否则如果i!=+1,说明本次想-1//只有n>0时,才能-1if(this.n>0){this.n--;}}}}
});
</script>
事件对象
vue中如何获得事件对象 (vue中如何获得鼠标位置)
1). 如果事件处理函数不需要传入实参值时,则:
事件对象也是作为处理函数第一个参数值自动传入,也是在函数定义时,用一个形参e,就可接住——同DOM
示例: 使用e获得鼠标位置:
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title><style>div{width:300px; height:100px;margin:20px;}#d1{background-color:#aaf}#d2{background-color:#ffa}</style><script src="js/vue.js"></script>
</head>
<body>
<div id="app">
<div id="d1" @click="doit">d1</div>
<div id="d2">d2</div>
</div>
<script>
var vm=new Vue({el:"#app",data:{},methods:{doit(e){//同DOM的econsole.log(`点在d1的: x:${e.offsetX},y:${e.offsetY}`);}}
})
</script>
</body>
参数传递
<div id="app"><!--事件调用的方法没有参数 带不带括号一样--><button @click="btn1Click()">btn1Click()</button><button @click="btn1Click">btn1Click</button><button @click="btn2Click(123)">btn2Click(123)</button><button @click="btn2Click()">btn2Click()</button><!--在事件定义时,写方法时省略了小括号,但是方法本身是需要一个参数的,这个时候 Vue会默认将浏览器生成的event事件对象作为参数传入到方法--><!--鼠标事件 浏览器自动生成event对象--><button @click="btn2Click">btn2Click</button><!--在方法定义时,我们需要event对象,同时又需要其他参数--><!--此处必须写$event vue解析为event对象 如果写event会去data中找变量event不存在参数要带引号(字符串)不然也会去data中找该变量--><button @click="btn3Click('abc',$event)">btn3Click('abc',$event)</button><button @click="btn3Click(abc,$event)">btn3Click(abc,$event)</button>
</div>
<script src="vue.js"></script>
<script>const app = new Vue({el:'#app',data:{message:'你好'},methods:{btn1Click(){console.log("btn1Click");},btn2Click(event){console.log(event);},btn3Click(abc,event){console.log(abc,event);},}})
</script>
$event
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title><style>div{width:300px; height:100px;margin:20px;}#d1{background-color:#aaf}#d2{background-color:#ffa}</style><script src="js/vue.js"></script>
</head>
<body>
<div id="app">
<div id="d1" @click="doit('d1',$event)">d1</div>
<div id="d2" @click="doit('d2',$event)">d2</div>
</div>
<script>
var vm=new Vue({el:"#app",data:{},methods:{doit(name,e){//同DOM的econsole.log(`点在${name}的: x:${e.offsetX},y:${e.offsetY}`);}}
})
</script>
</body>
v-once
仅在首次渲染页面时绑定一次,即使之后模型变量再改变,也不会自动更新页面: <元素 v-once>
示例: 显示时间
<h1>当前系统时间: {{time}}</h1>
<!--希望上线时间只在首次打开网页时绑定一次,之后,即使time变量值发生变化,也不会自动更新上线时间-->
<h1 v-once>上线时间: {{time}}</h1>
</div>
<script>
var vm=new Vue({el:"#app",data:{time:new Date().toLocaleString()}
})
setInterval(function(){vm.time=new Date().toLocaleString()
},1000)
</script>
v-pre
防止元素内容中的{{}}被vue编译,让内容中的{{}}原样显示!
<元素 v-pre> xxx{{xx}}xxx </元素>
双向绑定
- 单向绑定: 只能将data中的变量值,自动同步更新到HTML页面中。但是,页面上的修改,无法自动更新回data的变量中。(前面的11种都是)
(data —> div#app 但是 div#app -x-> data) - 双向绑定: 既能将data中的变量值,自动同步更新到HTML页面中。又能将页面上的修改自动更新回data的变量中。
(data <===> div#app) - 何时使用双向绑定: 只有绑定表单元素时,才有必要用双向绑定!因为只有表单元素,用户才能在页面上修改的它的内容。
v-model
<div id="app"><!--在文本框上按回车可以查找--><input type="text" v-model:value="keywords" @keyup="myKeyUp"><!--点击按钮可以查找--><button @click="search">百度一下</button>
</div>
<script>
var vm=new Vue({el:"#app",data:{keywords:"macbook i5"},methods:{search(){console.log(`查找 ${this.keywords} 相关的内容...`)},// $event// ↓myKeyUp(e){//只有按回车才查找if(e.keyCode==13){//调用旁边的search()函数this.search();}}},//想变量keywords只要被更改,就重新执行一次搜索watch:{keywords(){console.log("自动调用一次watch中的keywords函数...")this.search();}}//进入new Vue()中的一切data:{}, methods:{}, watch:{}都会被打散,最终都直接隶属于new VUe()对象,都是平级的,所以可以this.方式,互访。
})
</script>
<div id="app">
<label><input type="radio" name="sex" value="1" v-model:checked="sex">男</label>
<label><input type="radio" name="sex" value="0" v-model:checked="sex">女</label>
<h1>sex:{{sex}}</h1>
</div>
<script>
var vm=new Vue({el:"#app",data:{sex:1}
})
</script>
(3). 双向绑定select元素: 因为改变select的选中项,改变的是中整个select的value值,所以应该绑定select元素的value
a.
<select v-model:value="变量"><option value="值1">文本1</option><option value="值2">文本2</option>... ...
<div id="app">
<select v-model:value="orderStatus"><option value="0">未付款</option><option value="10">已付款</option><option value="20">已发货</option><option value="30">已签收</option>
</select>
<h1>orderStatus:{{orderStatus}}</h1>
</div>
<script>
var vm=new Vue({el:"#app",data:{//0:未付款 10:已付款 20:已发货 30:已签收orderStatus:10}
})
</script>
<div id="app"><input type="text" placeholder="请输入用户名" :disabled="!isAgree"><br/><input type="password" placeholder="请输入密码" :disabled="!isAgree"><br/><label><input type="checkbox" v-model:checked="isAgree">同意</label></br><button :disabled="!isAgree">注册</button><h1>isAgree:{{isAgree}}</h1>
</div>
<script>
var vm=new Vue({el:"#app",data:{isAgree:false //表示不同意}
})
</script>
(5). 简写: 其实以上所有v-model后的":属性名"都可省略!v-model可自动根据所在的元素不同,选择对应的的元素自动绑定。
监控函数 watch
绑定样式
绑定内联样式
<div id="app"><div id="pop" style="position:fixed; width:100px; height:100px; background-color:pink" :style="popStyle"></div>
</div>
<script>
var vm=new Vue({el:"#app",data:{popStyle:{top:"0px",left:"0px"}//自动翻译为: popStyle:"top:0px; left:0px"}
})
window.onkeydown=function(e){//左: 37 上: 38 右: 39 下: 40if(e.keyCode==39){//右var left=parseInt(vm.popStyle.left);left+=20;vm.popStyle.left=left+"px";}else if(e.keyCode==40){//下var top=parseInt(vm.popStyle.top);top+=20;vm.popStyle.top=top+"px";}
}</script>
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title><script src="js/vue.js"></script>
</head>
<body>
<div id="app"><div id="pop" style="position:fixed; width:100px; height:100px; background-color:pink" :style="{top, left}"></div><!--自动翻译为: top:0px; left:0px;-->
</div>
<script>
var vm=new Vue({el:"#app",data:{top:"0px",left:"0px"}
});
window.onkeydown=function(e){//左: 37 上: 38 右: 39 下: 40if(e.keyCode==39){//右var left=parseInt(vm.left);left+=20;vm.left=left+"px";}else if(e.keyCode==40){//下var top=parseInt(vm.top);top+=20;vm.top=top+"px";}else if(e.keyCode==38){//上var top=parseInt(vm.top);top-=20;vm.top=top+"px";}else if(e.keyCode==37){//左var left=parseInt(vm.left);left-=20;vm.left=left+"px";}
}
</script>
</body>
</html>
绑定class
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title><script src="js/vue.js"></script><style>/* 定义提示框的基础样式 */.msg{display:inline-block;width:160px;height:25px;border:1px solid #555;text-align:center;line-height:25px;}/* 定义提示框在验证通过时的样式 */.success{border:1px solid green;background-color:lightgreen;color:green}/* 定义提示框在验证失败时的样式 */.fail{border:1px solid red;background-color:pink;color:red}</style>
</head>
<body>
<div id="app"><!-- 因为要获得用户输入的手机号进行验证,所有必须用双向绑定 --><input type="text" v-model="phone"><!-- 因为提示框的样式可能在success和fail之间来回切换,所以动态绑定span的部分class 又因为提示框的内容也可能随验证结果而动态变化,所以也要绑定一个变量msg--><span class="msg" :class="msgClass">{{msg}}</span><!--界面中共需要三个变量-->
</div>
<script>
var vm=new Vue({el:"#app",data:{//因为页面中需要三个变量,所以data中就要有三个变量phone:"", //实时接用户在文本框中输入的手机号msg:"", //根据验证结果的对错,绑定提示信息的内容//根据验证结果的对错,动态在success和fail两个class之间来回切换。msgClass:{success:false,fail:false}},watch:{//因为用户一边输入,vue就一边验证,所以必须用watch随时监控用于的输入变化。phone(){//定义正则验证phone的内容var reg=/^1[3-9]\d{9}$/;//如果用正则验证phone的内容通过if(reg.test(this.phone)==true){//就修改msgClass应用成功的样式,不应用失败的样式this.msgClass={success:true,fail:false}//修改span的提示信息内容this.msg="手机号格式正确!"}else{//否则如果验证失败//就修改msgClass应用失败的样式,不应用成功的样式this.msgClass={success:false,fail:true}//修改span的提示信息内容this.msg="手机号格式正确!"}}}
})
</script>
</body>
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title><script src="js/vue.js"></script><style>/* 定义提示框的基础样式 */.msg{display:inline-block;width:160px;height:25px;border:1px solid #555;text-align:center;line-height:25px;}/* 定义提示框在验证通过时的样式 */.success{border:1px solid green;background-color:lightgreen;color:green}/* 定义提示框在验证失败时的样式 */.fail{border:1px solid red;background-color:pink;color:red}</style>
</head>
<body>
<div id="app"><!-- 因为要获得用户输入的手机号进行验证,所有必须用双向绑定 --><input type="text" v-model="phone"><!-- 因为提示框的样式可能在success和fail之间来回切换,所以动态绑定span的部分class 又因为提示框的内容也可能随验证结果而动态变化,所以也要绑定一个变量msg--><!-- class名 变量--><!-- <span class="msg" :class="{success:success, fail:fail}">{{msg}}</span> --><!--结果: 哪个class名后的变量值为true,才会进入最终的class字符串中。变量值为false的class,不会出现在最终的class字符串中--><span class="msg" :class="{success, fail}">{{msg}}</span><!--界面中共需要四个变量--><!--但是,两个class和错误提示三个变量的值,都和验证结果这一个数据有关!所有,其实只用一个变量就可控制三个值的变化-->
</div>
<script>
var vm=new Vue({el:"#app",data:{//因为页面中需要三个变量,所以data中就要有三个变量phone:"", //实时接用户在文本框中输入的手机号msg:"", //根据验证结果的对错,绑定提示信息的内容//根据验证结果的对错,动态在success和fail两个class之间来回切换。success:false,fail:false},watch:{//因为用户一边输入,vue就一边验证,所以必须用watch随时监控用于的输入变化。phone(){//定义正则验证phone的内容var reg=/^1[3-9]\d{9}$/;//如果用正则验证phone的内容通过if(reg.test(this.phone)==true){//就修改msgClass应用成功的样式,不应用失败的样式this.success=true;this.fail=false//修改span的提示信息内容this.msg="手机号格式正确!"}else{//否则如果验证失败//就修改msgClass应用失败的样式,不应用成功的样式this.success=false;this.fail=true//修改span的提示信息内容this.msg="手机号格式正确!"}}}
})
</script>
</body>
iv. 示例: 优化: 尽量减少vue中data中的变量,便于维护。
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title><script src="js/vue.js"></script><style>/* 定义提示框的基础样式 */.msg{display:inline-block;width:160px;height:25px;border:1px solid #555;text-align:center;line-height:25px;}/* 定义提示框在验证通过时的样式 */.success{border:1px solid green;background-color:lightgreen;color:green}/* 定义提示框在验证失败时的样式 */.fail{border:1px solid red;background-color:pink;color:red}</style>
</head>
<body>
<div id="app"><!-- 因为要获得用户输入的手机号进行验证,所有必须用双向绑定 --><input type="text" v-model="phone"><!-- 因为提示框的样式可能在success和fail之间来回切换,所以动态绑定span的部分class 又因为提示框的内容也可能随验证结果而动态变化,所以也要绑定一个变量msg--><!-- class名 变量--><!-- <span class="msg" :class="{success:success, fail:fail}">{{msg}}</span> --><!--结果: 哪个class名后的变量值为true,才会进入最终的class字符串中。变量值为false的class,不会出现在最终的class字符串中--><!-- <span class="msg" :class="{success:isRight, fail:isRight==false}">{{isRight==true?"手机号可用":"手机号格式错误!"}}</span> --><span class="msg" :class="isRight==true?'success':'fail'">{{isRight==true?"手机号可用":"手机号格式错误!"}}</span><!--界面中共需要四个变量--><!--但是,两个class和错误提示三个变量的值,都和验证结果这一个数据有关!所有,其实只用一个变量就可控制三个值的变化-->
</div>
<script>
var vm=new Vue({el:"#app",data:{//因为页面中需要三个变量,所以data中就要有三个变量phone:"", //实时接用户在文本框中输入的手机号isRight:false},watch:{//因为用户一边输入,vue就一边验证,所以必须用watch随时监控用于的输入变化。phone(){//定义正则验证phone的内容var reg=/^1[3-9]\d{9}$/;//将验证结果保存到变量isRight中,依次牵连着三个位置发生变化!this.isRight=reg.test(this.phone);}}
})
</script>
</body>
自定义指令
向Vue大家庭中添加自定义指令:
Vue.directive(“指令名”, { //向Vue大家庭中添加一个新的指令,并制定指令名
//回调函数: 当渲染后的元素被插入到DOM树上之后,自动执行该回调函数
inserted(当前指令所在的DOM元素对象){
//对当前指令所在的DOM元素对象,执行DOM操作
//在这里对当前DOM元素执行的DOM操作,会自动应用到页面上
}
})
强调: 添加自定义指令时,指令名一定不要带v-前缀!比如,想添加一个让元素自动获得焦点的自定义指令,可命名为"focus"使用自定义指令: 只要在想应用对应效果的元素上,添加"v-指令名"属性即可。
强调: 虽然定义指令时,指令名没有加v-前缀,但是使用指令时,必须加v-前缀原理:
(1). Vue.directive(“指令名”,{
inserted(domElem){
对domElem执行原生DOM操作
}
})
向Vue大家庭中添加一个新的自定义指令,关联一个处理函数
(2). new Vue()时,会扫描<div id="app">
下的所有内容。
(3). 每扫描到一个v-开头的指令属性,就会回Vue大家庭中找是否有对应的指令。如果有对应的指令,就调用指令关联的处理函数,对指令所在的元素执行原生DOM操作。示例: 让文本框自动获得焦点:
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title><script src="js/vue.js"></script><script>Vue.directive("focus",{inserted(domElem){//让当前元素自动获得焦点domElem.focus();// DOM原生}})</script>
</head>
<body>
<div id="app"><input type="text" v-focus><button>百度一下</button>
</div>
<script>
var vm=new Vue({el:"#app",data:{}
})
</script>
</body>
强调: new Vue()起到最重要的扫描HTML内容的作用,只有new Vue()才认识v-开头的指令,所有,即使data中没有值,只要用到了Vue相关的功能,new Vue()都不能省略!
计算属性
methods和computed都可以实现同样的功能, 但是计算属性会进行缓存, 如果多次使用时, 计算属性只会调用一次
- 什么是: 程序中没有保存该属性的属性值,每次绑定属性时,都需要根据其他属性的值动态计算获得该属性的属性值。
- 为什么: 有些值,总是可以根据其他属性值的变化,动态计算获得。这样的值,就没必要在程序中再保存一份。因为就算保存了,这个属性所依赖的其他属性值一旦发生变化,这个属性的属性值连带也要改变。
- 何时: 今后,只要一个属性的值可以根据其他属性计算出来,都没必要保存!
- 如何:
(1). 定义计算属性:
new Vue({
el:"#app",
data:{
页面所需的变量
},
methods:{
自定义函数或事件处理函数
},
watch:{
监视某个变量的监视函数
},
computed:{
属性名(){
return 根据其他属性值计算后获得的计算结果
}
}
})
(2). 使用计算属性: 计算属性,虽然本质是一个函数,但是在HTML中绑定语法中使用时,不要加()! - 结果:
(1). 只要计算属性所依赖的另一个属性值发生改变,同样会通知计算属性重新计算新的属性值。
(2). 计算属性计算出的结果,会被Vue缓存起来,反复使用,避免重复计算。即使反复使用多次,也只在首次计算一次。 - 笔试: methods vs computed
(1). 用法:
a. methods必须加()
b. computed 不用加()
(2). 反复使用:
a. methods中的方法,每调用一次,就会重新执行一次,执行结果,不会被vue缓存起来。
b. computed中的计算属性,只在第一次使用时计算一次,然后,结算结果,就会被Vue缓存起来,即使在别的地方反复使用这个计算属性,也不会重复计算,而是直接从缓存中获取值。但是,当所依赖的其他属性值发生变化时,计算才被迫重新计算一次。 - 如何选择methods和computed:
(1). 如果这次使用时,更关心函数的执行结果数据时,首选计算属性
(2). 如果这次使用时,更关心函数执行操作的过程,结果数据无所谓,甚至函数没有返回值,则首选methods中的方法。
<div id="app"><!--1.直接拼接:语法过于繁琐--><h2>{{firstName}} {{lastName}}</h2><!--2.通过定义methods--><h2>{{getFullName()}}</h2><h2>{{getFullName()}}</h2><h2>{{getFullName()}}</h2><!--3.通过computed--><h2>{{fullName}}</h2><h2>{{fullName}}</h2><h2>{{fullName}}</h2>
</div>
<script src="vue.js"></script>
<script>const app = new Vue({el:'#app',data:{firstName: 'Kobe',lastName: 'Bryant'},methods: {getFullName: function () {console.log('getFullName' );return this.firstName + ' ' + this.lastName}},computed: {fullName: function () {console.log('fullName' );return this.firstName + ' ' + this.lastName}}})
</script>
<div id="app"><h3>总计: ¥{{total.toFixed(2)}}</h3><ul><li v-for="(item,i) of cart" :key="i">{{item.pid}} | {{item.pname}} | ¥{{item.price.toFixed(2)}} | {{item.count}} —— 小计:¥{{(item.price*item.count).toFixed(2)}}</li></ul><h3>总计: ¥{{total.toFixed(2)}}</h3>
</div>
<script>
var vm=new Vue({el:"#app",data:{cart:[{pid:1, pname:"华为",price:4455, count:2},{pid:2, pname:"小米",price:3455, count:1},{pid:3, pname:"OPPO",price:3356, count:3},]},methods:{ //叫方法,所以用法同方法的用法——必须加()},computed:{ //叫属性,所以用法同属性的用法——不加()total(){console.log("调用了一次total()");var sum=0;for(var p of this.cart){sum+=p.price*p.count;}return sum;}}
})
</script>
<div id="app"><!--' ' 里面加个空格--><h2>{{firstName + ' ' + lastName }}</h2><h2>{{firstName}} {{lastName}}</h2><h2>{{getFullName()}}</h2><!--使用计算属性,fullName当作属性直接使用 不用调用方法带小括号 --><h2>{{fullName}}</h2></div>
<script src="vue.js"></script>
<script>const app = new Vue({el:'#app',data:{firstName: 'Lebron',lastName: 'James'},computed: {fullName: function () {return this.firstName + ' ' + this.lastName}},methods:{getFullName(){return this.firstName + ' ' + this.lastName}}})
</script>
<script>const app = new Vue({el:'#app',data:{books:[{id:1, name: 'JavaScript权威指南',price:119},{id:2, name: 'JavaScript高级设计程序',price:119},{id:3, name: '你不知道的JavaScript',price:119},{id:4, name: 'JavaScript设计模式',price:119},]},computed: {totalPrice: function(){//filter/map/reduce// return this.books.reduce()let result=0for(let i=0;i<this.books.length;i++){result += this.books[i].price}// for(let i in this.books){// this.books[i]// }// for(let book of this.books){//// }return result}}})
</script>
计算属性的setter和getter(了解)
<div id="app"><h2>{{fullName}}</h2>
</div>
<script src="vue.js"></script>
<script>const app = new Vue({el:'#app',data:{firstName: 'Kobe',lastName: 'Bryant'},computed: {// fullName:function () {// return this.firstName+ ' ' + this.lastName// }// 属性:字符串// name: 'codewhy'// 属性:对象fullName: {// 计算属性一般是没有set方法,只读属性set: function (newValue) {const names = newValue.split('');this.firstName = names[0];this.lastName = names[1];},get:function () {// return 'abc'return this.firstName+ ' ' + this.lastName},fullName:function () {return this.firstName+ ' ' + this.lastName}}}})
</script>
过滤器
什么是: 专门对变量的原始值进行加工后,再显示的特殊函数
为什么: 个别变量的原始值不能直接给人看!
比如: 性别 0和1 日期的毫秒数何时: 如果一个变量的值不能直接给人看时,必须经过加工,才能给人看时
如何:
(1). 向Vue大家庭中添加过滤器函数
Vue.filter(“过滤器名字”, function(oldVal){ //接受一个变量的原始值
return 根据oldVal的不同,动态返回的新值
})
(2). 在绑定语法中使用过滤器函数:
{{变量 | 过滤器名 }}结果:
(1). 变量的原始值不会立刻显示出来,而是先交给|后的过滤器函数
(2). 再将过滤器处理后的返回值,返回出来,显示在元素内容中原理:
(1). Vue.filter(“过滤器名”, function(oldVal){ return 新值 })
定义一个过滤器函数,加入到Vue大家庭中备用
(2). 当new Vue()扫描到{{}}中的|时,会回Vue大家庭中查找相应名称的过滤器函数。
(3). 只要找到,就先将|前的变量原始值,交给过滤器函数的oldVal参数,经过过滤器函数的加工,返回新值。显示到当前绑定语法的位置。示例: 过滤性别的1和0为男和女
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title><script src="js/vue.js"></script><script>//sexFilter=function(){ ... }Vue.filter("sexFilter",function(oldVal){//性别接住的旧值可能是1或0return oldVal==1?"男":"女"})</script>
</head>
<body>
<div id="app"><h1>性别: {{sex | sexFilter}}</h1>
</div>
<script>
var vm=new Vue({el:"#app",data:{sex:1}
})
</script>
</body>
- 过滤器可以加参数:
(1). 定义过滤器时:
Vue.filter(“过滤器名”,function(oldVal, 自定义形参, …){
Return 新值
})
(2). 使用过滤器时:
{{变量 | 过滤器名(自定义实参, …) }}
(3). 示例: 根据参数值返回不同语言的性别
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title><script src="js/vue.js"></script><script>//sexFilter=function(){ ... }Vue.filter("sexFilter",function(oldVal,language){//性别接住的旧值可能是1或0//language参数可能接住cn或en,其中,默认是cnif(language=="en"){return oldVal==1?"Male":"Female"}else{return oldVal==1?"男":"女"}})</script>
</head>
<body>
<div id="app"><h1>性别: {{sex | sexFilter}}</h1><h1>性别: {{sex | sexFilter("en")}}</h1>
</div>
<script>
var vm=new Vue({el:"#app",data:{sex:1}
})
</script>
- 过滤器可以连用:
(1). 绑定语法中: {{变量 | 过滤器1 |过滤器2 | … }}
(2). 强调:
a. 后一个过滤器2,进入的旧值,已经不是变量的原始值了,而是前一个过滤器加工后的中间值。
b. 只有最后一个过滤器的返回值,才会显示到页面上。如果希望前几个过滤器的返回值也能一起显示到页面上,只能在最后一个过滤器中将新值拼接到上一步过滤器传入的旧值上。
(3). 示例:为性别再额外添加图标
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title><script src="js/vue.js"></script><script>//定义一个新的过滤器专门为性别添加图标Vue.filter("sexIcon",function(oldVal){//oldVal可能有六种:1 0 男 女 Male Femaleif(oldVal==1||oldVal==0){return oldVal==1?"♂":"♀";}else{return oldVal=="男"||oldVal=="Male"?oldVal+"♂":oldVal+"♀";}});//sexFilter=function(){ ... }Vue.filter("sexFilter",function(oldVal,language){//性别接住的旧值可能是1或0//language参数可能接住cn或en,其中,默认是cnif(language=="en"){return oldVal==1?"Male":"Female"}else{return oldVal==1?"男":"女"}})</script>
</head>
<body>
<div id="app"><h1>性别: {{sex | sexFilter | sexIcon }}</h1><h1>性别: {{sex | sexFilter("en") | sexIcon }}</h1><h1>性别: {{sex | sexIcon }}</h1>
</div>
<script>
var vm=new Vue({el:"#app",data:{sex:1}
})
</script>
</body>
axios
- 什么是axios: 基于Promise的专门发送ajax请求的函数库
- 为什么: 总结发送ajax请求:
(1). xhr4步/6步
(2). 自己封装函数,考虑不全面
(3). jQuery中 $ .ajax: 问题,在vue中几乎不再使用DOM操作,几乎不用jQuery了。如果单是为了引入$.ajax函数而引入整个jQuery库,有点儿小题大做。
(4). Vue官方提供了一套发送ajax请求的组件: vue-resource,后来,Vue发现哪个框架都有自己的发送ajax请求就得函数,而且都大同小异,所以,Vue认为自己没有必要再重新开放按一套ajax函数库,所以vue-resource已经不再维护。
(5). Vue官方帮我们选了一个时髦好用的ajax函数库: axios,所以将来在框架中发送ajax请求,几乎都用axios。 - 何时: 只要在Vue框架中,发送ajax请求服务器端数据,都用axios
- 如何:
(1). 准备: 在项目中引入axios.js,才能引入axios函数库
<script src="js/axios.js">
引入的顺序和vue.js无关
(2). 设置所有服务器端接口的公共域名部分
axios.defaults.baseURL=“服务器端域名地址部分”
(3). 发送get请求:
axios.get(“url”,{ //向服务器端接口地址url发送get请求
params:{ //携带参数
参数1: 值1,
… : …
}
//自动翻译为"?参数1=值1&参数2=值2&…"
//}).then(function(result){
}).then(res=>{
res.data才是服务器端返回的结果
})
(4). 发送post请求:
axios.post(“url”, “变量1=值1&变量2=值2&…”)
.then(res=>{
res.data时服务器端返回的结果
})
(5). 示例: 使用axios向新浪云上的接口地址发送get和post请求,传参,并接受响应结果
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title><script src="js/axios.min.js">//axios={ // get(){ ... },// post(){ ... }//}</script><script src="js/qs.min.js">//专门就将对象语法转为查询字符串语法//{ uname:"dingding", upwd:"123456"}// ↓ Qs.stringify(对象) //"uname=dingding&upwd=123456"</script>
</head>
<body><script>//先定义所有接口统一的服务器端域名部分axios.defaults.baseURL="http://xzserver.applinzi.com"//向服务器端请求学子商城首页商品数组,包含6个商品对象的信息axios.get("/index").then(res=>{console.log(res.data);})//想获取5号商品的详细信息: 要求: 携带一个lid参数,值为要查询的商品编号axios.get("/details",{params:{lid:5}}).then(res=>{console.log(res.data) //返回5号商品的详细信息})//用用户名dingding,密码123456,调用登录接口axios.post("/users/signin",//"uname=dingding&upwd=123456"Qs.stringify({ uname:"dingding", upwd:"123456"})).then(res=>{console.log(res.data);})</script>
</body>
组件
什么是: 页面中拥有专属的HTML、CSS、js和数据的可重用的独立功能区域
为什么: 1. 重用!2. 便于大项目的分工协作!3. 松耦合!
何时: 只要页面中出现一个功能,可能被反复使用,或需要多人分工协作时,都用组件。
如何创建一个组件:
强调: 因为HTML标签名不区分大小写,所组件名如果包含多个单词,绝对不能用驼峰命名!必须用-分割多个单词。
比如: myCounter,错的。因为myCounter可能被转换为全大写或全消息的标签名,使用时会有歧义。
my-counter,对的。不会被转换。如何使用组件: Vue中的组件本质就是一个可重用的自定义HTML标签而已
在页面中<组件名></组件名>原理:
(1). Vue.component(“my-counter”,{})其实是向当前页面的Vue大家庭中添加一个Vue组件对象。
(2). new Vue()在页面中扫描到一个自定义标签时,就会回vue的大家庭中查找是否有同名的组件
(3). 如果找到这个组件,就先复制组件的模板中的HTML片段,代替页面中自定义标签所在位置。
(4). 为本次组件的使用,临时调用一次data()函数,返回一个本次组件使用专属的数据对象。
(5). 创建组件对象,监控当前组件的小区域。
new Vue() vs 组件
示例: 定义修改数量的组件,并重用
(1). 1_my-counter.js
Vue.component("my-counter",{//大多数属性同new Vue()//1. 定义可反复使用的组件的HTML片段模板template:`<div><button @click="change(-1)">-</button><span>{{count}}</span><button @click="change(+1)">+</button></div>`,//2. 定义data()函数,可返回一个新的数据对象data(){return {//相当于以前new Vue()中的data{}//因为组件模板中需要一个变量countcount:0}},//3. 之后的内容和new Vue()中就完全一样了methods:{change(i){this.count+=i;this.count<0&&(this.count=0)}}
})
(2). 1_component.html
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title><script src="js/vue.js"></script><script src="1_my-counter.js">//Vue.component("my-counter",{ ... })</script>
</head>
<body>
<div id="app"><my-counter></my-counter><my-counter></my-counter><my-counter></my-counter>
</div>
<script>
var vm=new Vue({el:"#app",data:{}
})
</script>
</body>
- 其实,当组件的template模板被替换到页面上之后,new Vue()是不放心的。万一这次新加载的组件内容中又包含内嵌的不认识的标签和指令属性怎么办?
所以,new Vue()会重新扫描本次替换上来的模板HTML片段的内容。如果又包含不认识的指令,就再次去Vue家庭中查找指令。如果又遇到不认识的标签就再次去Vue大家庭中查找组件
组件化开发
什么是: 今后一个网页都是由组件拼接而成
为什么: 1. 重用!2. 便于大项目的分工协作!3. 松耦合!
何时: 今后几乎所有的项目都是组件化开发完成的
如何:
(1). 拿到一个网页后,先划分网页中的组件共有多个少,以及包含关系是什么
(2). 创建多个组件js文件,每个组件js文件中都创建一个组件对象
(3). 父组件中可用子组件标签,在父组件内部插入子组件
(4). 所有组件的js文件,应该都引入new Vue()所在的网页中原理:
(1). 网页中new Vue()扫描到<div id="app">
下不认识的标签,就回Vue大家庭中查找是否包含该组件定义
(2). 如果找到该组件定义,则用组件的template,替换页面中组件标签所在的位置
(3). new Vue()绝不是只扫描一次,而是每替换一次组件的模板片段,就重新扫描新加入的模板片段中,是否又包含更子级的不认识的标签。
(4). 只要扫描到不认识的标签,就会继续回Vue大家庭中,查找是否包含该组件定义。然后用组件的模板片段,代替刚才组件中不认识的标签出现的位置。依次类推,直到所有标签new Vue()都认识,也就是所有标签都变成浏览器原生HTML的标签后,new Vue()才停止扫描。
定义子组件
Vue.component("todo",{template:`<div><h1>待办事项列表</h1><todo-add></todo-add><todo-list></todo-list></div>`,components:{//规定todoAdd和todoList两组件只能在当前父组件todo内使用todoAdd, todoList
//vue会自动将驼峰命名的组件对象名,翻译为-分割
// ↓ ↓
//<todo-add> <todo-list>}//表示今后todoAdd和todoList只能在todo内使用
})
//定义子组件不要用Vue.component(),只能创建普通对象,且对象名必须用驼峰命名
var todoAdd={template:`<div><input type="text"/><button>+</button></div>`
}
var todoList={ template:`<ul><todo-item></todo-item><todo-item></todo-item><todo-item></todo-item></ul>`,components:{ //规定todoItem组件只能在当前父组件todoList内使用todoItem }
}
//对象的变量名,必须使用驼峰命名
//且,对象的变量名,将来会被自动翻译为子组件的标签名。
var todoItem={//{}内保持组件的内容格式不变。template:`<li>1 - 吃饭 <a href="javascript:;">x</a></li>`
}
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title><script src="js/vue.js"></script><!--子组件一定要在父组件之前引入!--><script src="2_todo-add.js">//Vue.component("todo-add",{ ... })</script><script src="2_todo-item.js">//Vue.component("todo-item",{ ... })</script><script src="2_todo-list.js">//Vue.component("todo-list",{ ... })</script><script src="2_todo.js">//Vue.component("todo",{ ... })</script>
</head>
<body>
<div id="app"><todo></todo>
</div>
<script>
var vm=new Vue({el:"#app",data:{}
})
</script>
</body>
组件间传递数据
Vue.component("todo",{template:`<div><h1>待办事项列表</h1><todo-add></todo-add><!--爹todo,通过:绑定语法,将自己data中的变量tasks,赋值给子组件todo-list的自定义属性tasks中--><todo-list :tasks="tasks"></todo-list></div>`,data(){return {tasks:["吃饭","睡觉","打亮亮","学习"]}},components:{todoAdd, todoList
//vue会自动将驼峰命名的组件对象名,翻译为-分割
// ↓ ↓
//<todo-add> <todo-list>}//表示今后todoAdd和todoList只能在todo内使用
})
var todoList={template:`<ul><!--比如: 名为tasks兜里的爹的tasks数组照样可用于当前子组件中v-for遍历--><!--如果当前todoList的子组件todoItem中继续需要todoList中的数据,也可通过:绑定语法,由当前组件放入子组件上的自定义属性中。可以放进多个自定义属性中,像多个口袋一样--><!--比如:todo-item子组件主要当前组件遍历出的任务名task和任务序号i,所以在todo-item上添加两个自定义属性,绑定task和i变量的值--><todo-item v-for="(task,i) of tasks" :task="task" :i="i" :key="i"></todo-item></ul>`,//孩子todoList组件,从爹给的名为tasks的兜里掏出爹给的tasks数组,就像使用自己的tasks数组一样使用。props:["tasks"],components:{ todoItem }
}
//对象的变量名,必须使用驼峰命名
//且,对象的变量名,将来会被自动翻译为子组件的标签名。
var todoItem={//{}内保持组件的内容格式不变。template:`<li><!--从爹给的口袋里获得的数据,可在孩子里用于绑定语法中,就像用自己data中的数据一样方便-->{{i+1}} - {{task}} <a href="javascript:;">x</a></li>`,//孩子todoItem组件,可从爹给的两个口袋task和i中取出爹给的两个值:任务名和任务序号。props:[ "task", "i" ]
}
SPA: Single Page Application
- 什么是: 整个应用程序只有一个完整的HTML页面,其它所谓的页面,其实都是组件而已。所谓的切换页面或跳转页面,其实都是在一个页面内,切换不同的组件而已。
- 何时: 几乎移动端的项目都用单页面应用
- 为什么:
- 如何:
(1). 只创建一个唯一完整的HTML页面,包含new Vue()
(2). 创建其他"页面"对应的组件文件
强调: 在SPA应用当中,所谓的"页面",其实都是包含页面片段内容的子组件
(3). 创建路由对象,包含路由字典:
a. 路由器对象: 随时监控地址栏变化,并能根据路由字典中规定,查找对应组件,替换唯一完整的HTML页面中指定区域 的对象。
b. 路由字典: 包含所有相对路径及其对应的组件对象名的数组
c. 路由器中必须包含路由字典才能正常工作
d. 如何:
1). 引入<script src="js/vue-router.js">
2). 创建路由器对象和路由字典
var router=new VueRouter({routes:[{path:"/", component: Index},{path:"/details", component: Details},{path:"/products", component: Products},{path:"*", component: NotFound }]})
<div id="app"><my-header></my-header><router-view></router-view></div>
var router=new VueRouter({routes:[...{path:"/details/:lid", component: Details, props:true}] 参数名 将参数lid的值直接变成props中的属性结果: 从此,想进入/details,必须带参数值!不带参数值不让进!
})
(2)
7. 示例: 实现单页面应用
(1). 3_SPA.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title><script src="js/vue.js"></script><script src="js/vue-router.js"></script><script src="3_index.js"></script><script src="3_details.js"></script><script src="3_products.js"></script><script src="3_not_found.js"></script><script src="3_router.js">//var router=new VueRouter({ ... })</script><script src="3_my_header.js"></script>
</head>
<body>
<div id="app"><my-header></my-header><router-view></router-view>
</div>
<script>
var vm=new Vue({el:"#app",data:{},//router:routerrouter
})
</script>
</body>
</html>
var Index={template:`<div style="background-color:#C8BFE7"><h1>这里是首页</h1><button @click="goToDetails">查看1号商品的详情</button><br/><router-link to="/details/2">查看2号商品的详情</router-link></div>`,methods:{goToDetails(){this.$router.push("/details/1")}}
}
var Details={template:`<div style="background-color:#FF7F27"><h1>这里是详情页</h1><h2>显示{{lid}}号商品的详细信息...</h2><button @click="back">返回首页</button></div>`,props:[ "lid" ],methods:{back(){this.$router.push("/")}}
}
var Products={template:`<div style="background-color:#B5E61D"><h1>这里是商品列表页</h1></div>`
}
var NotFound={template:`<h1 style="color:red">您访问的页面不存在!<br>404:Not Found</h1>`
}
var router=new VueRouter({routes:[{path:"/", component: Index},{path:"/details/:lid", component: Details,props:true},{path:"/products", component: Products},{path:"*", component: NotFound }]
})
Vue.component("my-header",{template:`<div><h1>这里是页头</h1><ul><li><router-link to="/">首页</router-link></li><li><router-link to="/products">商品列表页面</router-link></li></ul></div>`
})
VUE脚手架
- 什么是: 已经包含核心功能的半成品项目
- 为什么:
(1). 简单: 已经包含了核心功能,避免了大量重复编码
(2). 规范: 文件夹结构和文件夹名,文件名都已标准化,降低了不同项目之间的差异。 - 何时: 今后几乎所有前端项目的开发都是用脚手架完成的。
安装生成脚手架代码的工具 vue create
如何:
(1). 安装生成脚手架代码的工具:npm i -g @vue/cli
安装之后: 输入vue -V
, 看到版本号就算成功
(2). 用生成脚手架代码的工具为本次项目创建一套脚手架代码结构:
a. 在想要生成项目的目录,地址栏里写cmd,打开命令行
b. 在命令行中输入vue create xzvue
c.Your connection to the default npm registry seems to be slow.
你现在的链接是链接到默认的国外的npm仓库,看起来有些慢
Use https://registry.npm.taobao.org for faster installation?
(Y/n)
是否使用国内的淘宝镜像来更快速安装 输入Y按回车
d.? Please pick a preset:
请选择一个预置的设置
default (babel, eslint)
//使用默认设置
>
Manually select features
//手动选择功能
按方向键下,选第二个,手动选择功能,按回车
e.? Check the features needed for your project: (Press <space> to select, <a> to toggle all
选择你的项目需要的功能(按空格选中或取消选中,按a全选所有)
,<i>to invert selection)
(*
) Babel 将浏览器不认识的ES6甚至更高版本的js代码,翻译为ES5版本的js
( ) TypeScript
( ) Progressive Web App (PWA) Support
(*
) Router VUE中实现单页面应用的核心——路由器组件
(*
) Vuex 管理共享状态
( ) CSS Pre-processors
>
( ) Linter / Formatter Linter 是代码质量检查工具,会将格式不规范也报错!一定不要选择
( ) Unit Testing
( ) E2E Testing
按方向键上下移动,移动到想要选中的功能上,按空格键选中Babel、Router、Vuex,取消选中Linter
f.Use history mode for router? (Requires proper server setup for index fallback in production) (Y/n)
输入n 按回车
是否使用history模式作为路由器的标识(生产环境中需要配置首页重定向才能使用)
Vue-router默认采用#/相对路径 方式实现客户端路由地址:
比如: http://localhost:5500/index.html#/
http://localhost:5500/index.html#/details/2
history模式:
http://localhost:5500/
http://localhost:5500/details/2
默认浏览器会将所有地址栏中的地址发给服务器端请求资源,只要#地址留在客户端,不发给服务器。
但是,http://localhost:5500/details/2 我们本来想在客户端路由切换页面组件,但是浏览器也会发送给服务器端查找,因为服务器端没有这个资源,所以,很可能报错!
所以,将来,要么就用默认#/相对路径方式,要么可用histroy,必须同时请服务器端修改配置!
g.Where do you prefer placing config for Babel, ESLint, etc.? (Use arrow keys)
你想把本次的配置保存在哪个位置?(用方向键选择)
In dedicated config files
//把每种组件的配置,放在各自单独的配置文件中
> In package.json
//将所有配置集中放在一个package.json文件中
按方向键下,选in package.json,按回车
h.Save this as a preset for future projects? (y/N)
是否保存这次的配置为将来项目的预定义配置
输入N,按回车
i. 看到结果: 说明用脚手架工具生成项目源代码结构成功
…
Successfully created project xzvue.
【Vue】黑马Vue入门到高级实战汇总相关推荐
- 前端《Vue.js从入门到项目实战》PDF课件+《微信小程序实战入门第2版》PDF代码调试
JS进行开发,正如一切的编程语言都立足于电元信号的正负极,即01码,可为什么软件都不采用二进制编码来 进行开发呢?这里面牵扯到一个成本的问题,这正是影响项目领导者进行决策的关键因素.Vue项目与原生J ...
- 2018年最新Vue从基础入门到项目实战视频教程网盘学习地址
2018年最新Vue从基础入门到项目实战视频教程网盘学习地址: https://pan.baidu.com/s/15D6GiHnSgA5Eo0n9G5Ws1A 若网盘地址失效,可访问此地址(下单有网盘 ...
- 接口自动化测试从入门到高级实战(最新干货)
一.接口测试背景和必要性 接口测试是测试系统组件间接口(API)的一种测试,主要用于检测内部与外部系统.内部子系统之间的交互质量,其测试重点是检查数据交换.传递的准确性,控制和交互管理过程,以及系统间 ...
- .NET Core实战项目之CMS 第六章 入门篇-Vue的快速入门及其使用
写在前面 上面文章我给大家介绍了Dapper这个ORM框架的简单使用,大伙会用了嘛!本来今天这篇文章是要讲Vue的快速入门的,原因是想在后面的文章中使用Vue进行这个CMS系统的后台管理界面的实现.但 ...
- ❤️《Vue前端基础框架集合从入门到高级》(小白也可学,建议收藏)❤️
<Vue前端基础框架集合从入门到高级>,小白也可学 文章目录 <Vue前端基础框架集合从入门到高级>,小白也可学 ❤️一.前端核心分析 ❤️1.1.概述 ❤️1.2.前端三要素 ...
- 视频教程-Vue-cli3.x从入门到项目实战视频课程-Vue
Vue-cli3.x从入门到项目实战视频课程 北京八维研修学院技术工程师,5年大型项目实战开发经验,3年授课经验. 孟宪杰 ¥68.00 立即订阅 扫码下载「CSDN程序员学院APP」,1000+技术 ...
- 今天,学会这几个Vue高级实战技巧就够了!
前言 今天,我们来分享几个不可不知的vue高级实战技巧. 技巧 自动注册组件 我们平时可能这样引入注册组件.每次都得需要在头部引入,然后注册,最后在模板上使用. <template>< ...
- 最全16套vue.js入门和项目实战+素材+源码
vue.js实战项目17个 包括 电商实战 音乐播放器 团购网 新闻客户端 图书管理 移动端APP 点餐系统 小米阅读开发 商城 vue+Python前后端分离打造电商系统 vue+node构建大型商 ...
- 黑马VUE快速入门笔记
VUE 官网(https://v3.cn.vuejs.org/) 点击直接进入 我的第一个vue程序 <!DOCTYPE html> <html lang="en" ...
最新文章
- Linux基本命令之vi
- 关于大小型项目如何最大限度提高WebAPi性能
- 其它数据类型和Json的转化
- 百度SEO快克工具包 1.5.0 官方版
- 缓存世界中的三大问题及解决方案
- add p4 多个文件_绘图技巧01:继承特性创建对象之神器ADD
- 【转载】MyBatis+MySQL 返回插入的主键ID
- 解锁网易云音乐客户端变灰歌曲的详细教程
- python字符串赋值列表_Python 第二篇:python字符串、列表和字典的基本操作方法...
- [收藏]31部黑客电影
- JavaScript-作用域和作用链
- 获取字符串被分割后的总数组长度 java 类似UBound()方法
- 为什么ppt图形卡配置不正确_PPT常见问题解决方法,PPT检测到图形卡可能配置不正确怎么办?...
- 读书笔记--交流电的瞬时值和有效值
- Linux SPI驱动框架(2)——控制器驱动层
- sourcetree 中文版
- 利用FileReader和FileWriter完成一个文件拷贝功能
- 机器学习系列笔记一:Introduction
- 【ICPC模板】多元一次不定方程(丢番图方程)求解
- CentOS 8构建桌面办公环境
热门文章
- 前端《Vue.js从入门到项目实战》PDF课件+《微信小程序实战入门第2版》PDF代码调试