框架和插件
- 框架不能轻易换,库可以
MVC和MVVM
node(后端)中MVC:后端的分层开发概念
MVVM是前端视图层的概念,主要关注视图层分离,把前端视图层分成三部分:Model、view、vm viewmodel
VUE基础
一、数据与方法
- 当数据改变时,视图会进行重渲染。值得注意的是只有当实例被创建时就已经存在于 data 中的属性才是响应式的。
- 使用 Object.freeze(),会阻止修改现有的属性,也意味着响应系统无法再追踪变化。
var obj = { foo: 'bar' } Object.freeze(obj) new Vue({ el: '#app', data: obj }) <div id="app"> <p>{{ foo }}</p> <!-- 这里的 `foo` 不会更新! --> <button v-on:click="foo = 'baz'">Change it</button> </div>
- 不要在选项属性或回调上使用箭头函数,因为箭头函数并没有 this,this 会作为变量一直向上级词法作用域查找,直至找到为止,经常导致错误。
-
动态参数表达式:从 2.6.0 开始,可以用方括号括起来的 JavaScript 表达式作为一个指令的参数:
<a :[attributeName]="url"> ... </a>
- 动态参数表达式有一些语法约束,因为某些字符,如空格和引号,放在 HTML attribute 名里是无效的。
- 还需要避免使用大写字符来命名键名,因为浏览器会把 attribute 名全部强制转为小写:
二、指令
- v-cloak <style>[v-cloak]{display:none }</style> 解决插值表达式闪烁问题
- v-text:没有闪烁问题,会把标签中内容覆盖v-text=“msg”
- v-html:渲染成html
- v-once:只拿一次数据进行显示,后面数据更改,页面不会跟着更改
- v-pre:不解析{{}},直接显示
事件修饰符
- 常见事件修饰符
- @click.stop=""----阻止事件冒泡
- @click.prevent ----阻止默认行为
- @click.self ----只允许自身触发
- @click.captures----实现捕获触发机制
- @click.once----只执行一次
- @keyup.enter----回车键
- 使用修饰符时,顺序很重要;相应的代码会以同样的顺序产生。因此,用 v-on:click.prevent.self 会阻止所有的点击,而 v-on:click.self.prevent 只会阻止对元素自身的点击。
三、按键修饰符
- Vue.config.keyCodes.f2 = 113 使用 @keyup.f2
- 为了在必要的情况下支持旧浏览器,Vue 提供了绝大多数常用的按键码的别名: .enter、 .tab、 .delete (捕获“删除”和“退格”键)、 .esc、 .space、 .up、 .down、 .left、 .right
四、内联样式style(v-bind,语法糖:)
- 对象就是无序键值对的集合 "{'color‘:’red','font-size':'50px'}"
- 直接传递对象,对象在data中定义
- 在data中定义多个样式对象,以数组的方式传给style
五、class类属性(v-bind,语法糖:)
- 使用方式一:直接传递一个数组,数组中的需要单引号 "['more','add']"
- 使用方式二: 在数组中使用三元表达式
"['more','add',{'another':item.age<30}]" - 使用方式三:直接传递对象 “{active:true,thin:false}” 对象属性为类名
六、条件渲染(v-if 、v-else-if、 v-else、v-show)
- v-if 每次都会重新创建或删除
- v-show,不会重新创建或删除,只是切换display属性
- 元素涉及频繁的切换,使用v-show。
七、列表渲染(v-for)
- v-for="( item, index ) in list"
- 顺序:值-键-索引
- in后面放数组,表示循环几次
- 在组件中使用v-for时,需要指定key属性
- 如果不添加key,则在数组中新增元素的时候,创建虚拟dom会进行对比,直接把原来4的li的值改成0000,4以后的往后移动;添加以后会插入0000的li(提高性能)
<div id="app"> <ul> <li v-for="item in thingArray">{{item}}</li> </ul> <button @click="addItem">在3-4中间添加元素</button> </div> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script> const app = new Vue({ el: "#app", data: { thingArray: [1, 2, 3, 4, 5, 6, 7] }, methods: { addItem() { this.thingArray.splice(2, 0, '0000') console.log(this.thingArray) } } }) </script>
八、v-model的使用
<body>
<div id="main">
<input type="text" v-model="inputValue">
<span>输入框的值是:{{inputValue}}</span>
<h3>2.v-model的原理</h3>
<input type="text" :value="inputValue" @input="changeInputValue">
<span>输入框的值是:{{inputValue}}</span>
<h2>3.v-model和radio</h2>
<label for="male">
<input type="radio" id="male" name="sex" value="男" v-model="gender">男
</label>
<label for="female">
<input type="radio" id="female" name="sex" value="女" v-model="gender">女
</label>
<h5> ---您选择的性别是:{{gender}}</h5>
<h2>4.v-model和checkbox</h2>
<label for="agree">
<input type="checkbox" id="agree" v-model="isAgree">知情同意
</label>
<h4>是否选择{{isAgree}}</h4>
<button :disabled="!isAgree">下一步</button>
<h2>5.v-model 和 checkbox的多选框</h2>
<label v-for="hobby in constHobby">
<input type="checkbox" :id="hobby" :value="hobby" :key="hobby" v-model="chooseHobby"> {{hobby}}
</label>
<h5>你选择的爱好是:{{chooseHobby}}</h5>
<h2>6.v-model和select</h2>
<select name="" id="" v-model="selectHobby">
<option v-for="hobby in constHobby" :value="hobby" >{{hobby}}</option>
</select>
<h5>你选择的爱好是:{{selectHobby}}</h5>
<h2>7.v-model和select多选</h2>
<select name="" id="" v-model="selectHobbyArray" multiple>
<option v-for="hobby in constHobby" :value="hobby" >{{hobby}}</option>
</select>
<h5>你选择的爱好是:{{selectHobbyArray}}</h5>
<h2>8.v-model的修饰符</h2>
<!-- 懒加载,只有在按回车和失去焦点的时候会重新加载该数据 -->
<input type="text" v-model.lazy="lazyValue">
<span>输入框的值:{{lazyValue}}</span>
<!-- 去掉前后空格 -->
<input type="text" v-model.trim="trimValue">
<span>输入框的值:{{trimValue}}</span>
<!-- 数据类型改为number -->
<input type="text" v-model.number="numberValue">
<span>输入框的值:{{numberValue}} {{typeof numberValue}}</span>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
const app = new Vue({
el: "#main",
data: {
inputValue: "111",
gender: "男",
isAgree: false,
constHobby: ['篮球', '羽毛球', '乒乓球', '足球'],
chooseHobby: [],
selectHobby: '篮球',
selectHobbyArray:[],
lazyValue:'',
trimValue:"",
numberValue:""
},
methods: {
changeInputValue(event) {
this.inputValue = event.target.value;
}
}
})
</script>
</body>
九、computed:计算属性
- 包含本实例里面的变量发生变化触发(数据联动)
- 在html中
<div id="box">
<label>firstName:</label><input type="text" v-model="firstName">+
<label>lastName:</label><input type="text" v-model="lastName">=
<label>fullName: </label><input type="text" v-model="fullName">
</div>
- 在JS中
<script src="js/vue.js"></script>
<script>
new Vue({
el:'#box',
data:{
firstName: '',
lastName: ''
},
computed:{
//计算属性的本质是对象,有一个get方法和set方法,我们一般不同set方法(没有set方法,相当于一个只读属性)
fullName:{
get:function(){
return this.firstName+'-'+this.lastName
},
set:function(){}
}
// 没有set方法,可以直接如下写
fullName:function () {
return this.firstName+'-'+this.lastName
}
}
})
</script>
- 特点:
- 计算属性在引用的时候,不需要加()来调用,直接把他当成是普通的属性使用
- 和methods的区别:只要在计算属性的function内部,所用到的data中的数据发生变化,computed就会重新计算这个 计算属性,当触发重新渲染时,调用方法将总会再次执行函数。
- 计算属性求值的结果会被缓存起来,方便下次使用;如果计算属性方法中的数据没有发生变化,则不会重新对这个 计算属性 求值(computed种的属性: 在页面中使用四次,只会调用一次,其余使用缓存显示)(methods 中的方法,在页面中使用四次,会调用四次)
watch :侦听(异步场景)
- 通常更好的做法是使用计算属性而不是命令式的 watch 回调
<script src="js/vue.js"></script>
<script>
new Vue({
el:'#box',
data:{
firstName: '',
lastName: '',
fullName: ''
},
watch:{
firstName: function (newValue) {
this.fullName = newValue +'----'+ this.lastName
},
lastName: function (newValue) {
this.fullName = this.firstName +'----'+ newValue
}
}
})
</script>
- 上面代码是命令式且重复的。
过滤器:
- 全局过滤器:Vue.filter('过滤器的名称',function (dateStr,formate) { } )
- 函数的第一个参数是管道前面传递过来的数据
- 私有的过滤器:new Vue({ el:'#box',data:{},
filters:{ dateFormate:function (dateStr, formate) {} } })
组件化
-
定义:为了拆分Vue实例的代码量,能够用不同的组件划分不同的功能块。
- 模块化:从代码逻辑的角度进行划分,方便代码分层开发,保证模块的职能单一。
- 组件化:从UI界面的角度进行划分,前端的组件化,方便UI组件的重用
-
创建组件的方式
- 创建方式一:使用Vue.extend({template:' '})来创建全局的组件。使用Vue.component('组件名称',创建出来的组件)
var myCompontnt = Vue.extend({ template:`<h2>这是用extend创建</h2>` }) Vue.component('myCompontnt',myCompontnt);
Vue.component('myCompontnt',Vue.extend({ template:`<h2>这是用extend创建</h2>` }));
- 创建方式二:Vue.component('组件名称',组件的模板template)
Vue.component('myCompontnt',{ template:`<h2>这是用extend创建</h2>` });
- 创建方式三:将template写在html中 Vue.component('组件名称',{template:'#id'})
-
组件注册
Vue.component('my-component-name',{ /* ... */ })
- 全局注册: Vue.component('component-a',{ /* ... */ })在注册之后可以用在任何新创建的 Vue 根实例 (new Vue) 的模板中
- 局部注册:
-
var ComponentA = { /* ... */ } var ComponentB = { /* ... */ } var ComponentC = { /* ... */ } //components 选项中定义你想要使用的组件: new Vue({ el: '#app', components: { 'component-a': ComponentA, 'component-b': ComponentB } })
- 局部注册的组件在其子组件中不可用
-
组件中的data
- 组件可以拥有自己的data数据,组件中的data是一个方法,而且必须返回一个对象
-
组件名的命名和使用
- 在使用驼峰命名定义一个组件时,在引用这个元素时使用kebab-case的方式。不使用驼峰则直接使用定义的名称。
组件切换可以使用<component :is="存组件名称的参数">通过改变参数
-
传递参数
- 父组件向子组件传递参数使用props
-
Vue.component('todo-item', { props: ['todo'], template: '<li>{{ todo.text }}</li>' }) /*使用*/ <todo-item v-for="item in groceryList" :todo="item" :key="item.id" ></todo-item>
- props中的数据是只读的,data中的数据是可读可写的
- prpos:使用驼峰命名法,在传参时需要使用其等价的短横线分隔命名
- prop 指定的值类型。以对象形式列出 prop。
props: { title: String }
- 在JavaScript 中对象和数组是通过引用传入的,所以对于一个数组或对象类型的 prop 来说,在子组件中改变这个对象或数组本身将会影响到父组件的状态。
- 子组件通过事件调用向父组件传值
<div id="app7"> <childNode @func="show"></childNode> </div> <template id="temp1"><!--这是样式--> <div> <h2>这里是子组件</h2> <input type="button" value="调用父组件函数" @click="emitfunction"> </div> </template> <script src="js/vue.js"></script> <script> Vue.component('childnode',{ template:'#temp1', data : function(){ return { user:[{id:1,name:'张三'},{id:2,name:'李四'}] } }, methods:{ emitfunction (){ this.$emit('func','111','222',this.user) } } }); var app7 = new Vue({ el:'#app7', data:{ msg:'父组件的数据', user:[] }, methods:{ show(msg1,msg2,data){ console.log('这是父组件的函数调用'+msg1+'---'+msg2) this.user = data console.log(this.user[0].id) } } }) </script>
-
使用ref获取组件元素和dom方法
<div id="app7"> <input type="button" value="使用ref" @click="getDomMesg"> <h2 ref="title">这是父组件的H2啊</h2> <childnode ref="child"></childnode> </div> <template id="temp1"> <div> <h2>这里是子组件</h2> </div> </template> <script src="js/vue.js"></script> <script> Vue.component('childnode',{ template:'#temp1', data : function(){ return { msg:'这是子组件的msg' } }, methods:{ show (){ console.log('调用了子组件的方法------') } } }); var app7 = new Vue({ el:'#app7', data:{ msg:'父组件的数据', user:[] }, methods:{ getDomMesg(){ console.log(this.$refs.title.innerText) console.log(this.$refs.child.msg) this.$refs.child.show() } } }) </script>
自定义全局指令
Vue.directive( 'focus',{
updated:function (el) { },//可执行多次,当VNode更新时使用
bind:function (el,bindding) { },//只执行一次,当指令绑定到元素时。第二个参数可获得传入的参数,bindding。value。和样式有关的操作。
inserted:function (el) { el.focus() }//只执行元素,当元素插入dom中。和JS行为有关的操作在这执行
})
- 定义私有指令
directives:{
'fontweight':{
bind:function (el,bindding){
el.style.fontWeight = bindding.value
}
}
}
- 简写:
'fontsize':function (el,bindding) {
el.style.fontSize = parseInt(bindding.value) +'px'
}
生命周期
- 生命周期钩子 = 生命周期函数 = 生命周期事件
- 创建期间的生命周期函数
- beforeCreate: data和methods中的数据都还未被初始化
- created: data和methods中的数据初始化完成,调用数据和方法最早在这个方法中
- beforeMount:模板在内存中已经编辑好了,但是还没渲染到页面上,页面中的元素还未被真正的替换,只是一些模板字符串。document.getElementbyId(‘info’).innerText----->获取不到
- mounted:实例创建期间的最后一个生命周期函数,实例完全创建好了,没有其他操作的话,这实例就不变。操作dom节点最早在mounted
- 实例运行期间的生命周期函数
- updated和beforeUpdate在data中数据发生改变时触发0次或多次。
- beforeUpdate执行时,页面中的数据还是旧的,data中数据是最新的,页面和最新的数据还未同步
- updated事件执行时,界面和data已经保持同步,都是最新的
- 销毁阶段的钩子
- beforeDestroy:Vue实例从运行阶段进入销毁阶段。实例上的所有data和methods以及过滤器、指令等都处于可用状态,此时还未真正销毁。
- destoryed。组件完全被销毁。组件中所有的数据等都不可以用。
v-resource发起get、post、jsonp请求
methods:{
getMessage (){
this.$http.get('http://vue.studyit.io/api/getlunbo').then(
( result ) => {
this.getMessageResult = result.body;
console.log(result.body)
})
},
postMessage () {
this.$http.post('http://vue.studyit.io/api/post',{headers:
{'Access-Control-Allow-origin': 'http://localhost:8080/resource'}},
{emulateJSON:true}).then(
( result ) => {
this.PostMessageResult = result.body;
console.log(result.body)
})
},
jsonpMessage (){
this.$http.jsonp('http://vue.studyit.io/api/jsonp').then(
( result ) => {
this.JsonpMessageResult = result.body;
console.log(result.body)
})
}
}
动画
- 在html中
<button @click="flag = !flag">toggle</button>
<transition>
<h2 v-show="flag">hello animation</h2>
</transition>
+在css中
<style>
.v-enter,
.v-leave-to{
transform: translateX(200px);
opacity: 0;
}
.v-enter-active,
.v-leave-active{
transition: all 0.8s ease;
}
</style>
- 在<transition name='disp'> 定义了name后,修改v-前缀为disp-
- 使用第三方类库animate.css 在
<transition enter-active-class='animated bounceIn '>
- 使用钩子函数完成半场动画
- 在html中
<button @click="flag = !flag">加入购物车</button>
<transition @before-enter="beforeEnter" @enter="Enter" @after-enter="afterEnter">
<div class="ball" v-show="flag"></div>
</transition>
- 在JS中
new Vue({
el:'#box',
data:{
flag:false
},
methods:{
beforeEnter (el){
el.style.transform = 'translate(0,0)'
},
Enter (el,done){
el.offsetLeft //没有实际作用,但是不写,没有显示动画效果
el.style.transform = 'translate(200px,300px)'
el.style.transition= 'all 1s ease'
done()//done是afterEnter函数的引用,不调用会延迟
},
afterEnter(el){//flag的作用是控制小球的隐藏和显示;跳过后半场动画
this.flag = !this.flag//直接使用opacity无法实现该功能
}
}
})
- <transition-group>实现列表动画
<div id="box"> <label>Id : <input type="text" v-model="id"></label> <label>Name : <input type="text" v-model="name"></label> <button @click="add">添加</button> <ul> <transition-group> <li v-for="item in list" :key="item.id" @click="del(item.id)">{{item.id}} and {{item.name}}</li> </transition-group> </ul> </div>
- style
<style> li{ width: 400px; height:30px; font-size: 20px; border: 1px dashed #999; margin: 5px; list-style: none; padding-left: 10px; } li:hover{ background-color: burlywood; } .v-enter,.v-leave-to{ opacity: 0; transform: translateY(80px); } .v-enter-active,.v-leave-active{ transition: all 3s ease; } .v-move{ transition: all 1s ease; } .v-leave-active{ position: absolute; } </style>
- style
- 动画-transition-group中appear和tag属性的作用
- appear 实现页面刚展示出来的时候的效果
- tag用来指定渲染成什么属性,没有添加默认渲染成<span>属性
- mode可以实现组件切换时进出的先后顺序
插槽
- 编译作用域:父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。
- 后备内容(默认的)内容)
- 具名插槽 <slot> 元素有一个特殊的特性:name
- 任何没有被包裹在带有 v-slot 的 template>中的内容都会被视为默认插槽的内容。任何没有被包裹在带有 v-slot 的 template>中的内容都会被视为默认插槽的内容。
- 插槽prop
<slot v-bind:user="user">{{ user.lastName }} </slot> - v-slot也有缩写,字符 “#”