说明:本文本来是提供给公司内开发阅读,有些地方不必参考
认识项目结构
详细的看juejin.cn/post/684490…(包含项目结构和webpack)
如果你有vue基础请跳到下一节
vue简单介绍(核心:采用简洁的模板语法来声明式地将数据渲染进 DOM 的系统)
先来一个例子
<!-- 指定vue管理内容区域(id为app的标签内区域),需要通过vue展示的内容都要放到这个元素中 通常我们也把它叫做边界 数据只在边界内部解析-->
<template>
<div id="app">{{ msg }}</div>
</template>
<script>
export default {
name:'app',
data() {
return {
msg:'hello vue'
}
},
mounted() {
console.log(this.msg)
}
}
</script>
- 先在data里面声明数据,再使用数据
- data必须以函数返回值的形式定义,这样复用组件就返回一份新的data
- 如果使用对象形式的话一个变就全部变(对象是引用类型)
- 在其他属性或者钩子函数中通过this.msg可用访问数据
下面所有语句基于上面的例子
-
模板语法(页面中绑定数据的方法)
- v-text='msg'或者{{msg}}
- 原理:更新DOM的textContent
- v-html
- 可输入的文本不要使用v-html,可能导致XSS攻击
- 原理:更新DOM的innerHtml
- 在模板语法中可以使用js表达式,如{{ msg.split('').reverse().join('') }}
- 只能使用单个表达式,注意不是js语句(const a = '1'这是js语句)
- 不建议在vue文件使用js表达式,建议写到计算属性
- v-text='msg'或者{{msg}}
-
属性绑定
- v-bind,语法糖为:(英文的冒号)
- 用于绑定html标签的所有属性(例如:title='msg')
- 组件传值使用v-bind(组件中有事例)
- 当绑定的数据或者表达式的值改变会响应式的作用于DOM
- 如果绑定对象,读取的是对象的toString()方法后返回的值,如果没有重写这个方法,绑定值为[object Object](例1)
- 绑定属性时加冒号是绑定变量或者变量表达式,不加冒号是绑定字符串(所以传入number和Boolean值常量时也需要加冒号)(例2)
例1
<script>
data() {
return {
obj:{
name:'yangf'
}
}
}
mounted() {
obj.toString = function() {
return obj.name;
}
}
</script>
<template>
<!--绑定变量-->
<div :title='obj'></div>
<!--不加toString方法编译为-->
<div title=[object Object]></div>
<!--加了toString方法编译为-->
<div title='yangf'></div>
</template>
例2
<script>
data() {
return {
msg:'hello',
num: 5
}
}
</script>
<template>
<!--绑定变量-->
<div :title='msg' :data='num'></div>
<!--tab为组件-->
<!--这里的title如果加:组件里面读到的是hello,不加冒号读到的是'hello'-->
<tab :num='5' :title="'hello'"></tab>
</template>
-
绑定方法
- v-on,语法糖@
- 用于添加事件,绑定的事件写到methods对象中
- v-on:click='clickBtn'或者v-on:click='clickBtn('参数',$event)'或者@click='clickBtn'
- v-on可以监听多个方法
<input type="text" v-on="{input:onInput,focus:onFocus}">- 可以添加键盘事件@keyup.13="submit"和@keyup.enter="submit"相同
- 事件修饰符
-
- .stop阻止冒泡,调用 event.stopPropagation()
- .prevent阻止默认行为,调用 event.preventDefault()
- .capture添加事件侦听器时使用事件捕获模式
- .self只当事件在该元素本身(比如不是子元素)触发时,才会触发事件
- .once事件只触发一次
- 修饰键(.ctrl等)、鼠标按键修饰符(.left等)
-
v-model
- 在表单元素(input、textarea等)上创建数据双向绑定(监听输入事件改变数据)
- 对于需要使用输入法的语言,在输入法组合文字的过程中得不到更新
- 修饰符:
-
- .lazy 通过这个修饰符,转变为在执行 change 事件再更新数据
- .number 自动将用户的输入值转化为数值类型
- .trim 自动过滤用户输入的首尾空格
v-model只是语法糖,不同的输入元素使用不同的property并抛出不同的事件
- text 和 textarea 元素使用 value property 和 input 事件
- checkbox 和 radio 使用 checked property 和 change 事件
- select 字段将 value 作为 prop 并将 change 作为事件
<input v-model="msg" /> //这一行等于下一行
<input v-bind:value="msg" v-on:input="msg = $event.target.value" />
-
v-for
- 循环渲染数组或者对象
- 推荐添加key属性
- vue默认“就地更新”原则,复用已有元素,若数据项顺序改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序
- 使用 key,VUE会基于 key 的变化重新排列元素顺序,并且会移除 key 不存在的元素。
- 更准确:因为带 key 就不是就地复用了,在 sameNode 函数 a.key === b.key 对比中可以避免就地复用的情况
- 更快速:利用 key 的唯一性生成 map 对象来获取对应节点,比遍历方式更快
-
v-if和v-show
- v-if根据表达式的值的真假条件,销毁或重建元素;有更高的切换开销;表达式初始值为false将不会渲染标签
- v-show:根据表达式之真假值,切换元素的 display CSS 属性;有更高的初始渲染开销,无论表达式真假初始就会渲染
-
样式处理 -class和style
- v-bind:class="expression" or :class="expression"
- expression为字符串、数组、对象
<!-- 1 -->
<div v-bind:class="{ active: true }"></div>
<!--解析后-->
<div class="active"></div>
<!-- 2 -->
<div :class="['active', 'text-danger']"></div>
<!--解析后-->
<div class="active text-danger"></div>
<!-- 3 -->
<div v-bind:class="[{ active: true }, errorClass]"></div>
<!--解析后-->
<div class="active text-danger"></div>
-
监听器和计算属性
- watch
-
- watch一般写成一个对象,键是需要观察的表达式,值是对应回调函数
- 不缓存,当表达式的值发生变化后,会调用对应的回调函数完成响应的监视操作
- watch支持异步;
- 函数接收两个参数,第一个参数是最新的值;第二个参数是输入之前的值;
- 监听数据必须是data中声明过或者父组件传递过来的props中的数据
-
- 父组件传递过来的props中的数据是不允许被改变的
- 最好不要监听整个对象,而是监听对象中的某个属性
data() {
return {
a:1,
person:{
age:18
}
}
},
watch:{
a: function(val, oldVal) {
// val 表示当前值
// oldVal 表示旧值
console.log('当前值为:' + val, '旧值为:' + oldVal)
},
// 监听对象中所有属性的变化
// 给对象的所有属性都加上这个监听器,但是这样性能开销就会非常大了
// 任何修改obj里面任何一个属性都会触发这个监听器里的handler
person: {
// 确认是否以当前的初始值执行handler的函数,组件加载立即执行函数
immediate: true,
handler: function (val, oldVal) { /* ... */ },
deep: true
},
// 优化方案
'person.age': {
immediate: true,
handler: function (val, oldVal) { /* ... */ }
}
}
-
computed
- 计算属性是基于它们的依赖进行缓存的,只有在它的依赖发生改变时才会重新求值
- 模板语法中不要放入太多的逻辑,否则会让模板过重、难以理解和维护,推荐使用计算属性
- computed中的属性不能与data中的属性同名,否则会报错
-
组件传值
- 父组件到子组件
- 通过子组件props属性来传递
- props可以是数组(指定传入的属性名即可)
- 也可以是对象(传入的属性可以指定默认值、是否必传等)
- 组件中从父级传来的属性值必须在组件中通过props属性显示指定
- 传递过来的props属性的用法与data属性的用法相同
- 父级传值使用v-bind绑定属性
- 通过子组件props属性来传递
- 子组件传值到父组件
- 子组件中通过$emit()触发自定义事件
- 父级接收使用v-on绑定方法
- 组件上的 v-model 默认会利用名为 value 的 prop 和名为 input 的事件
-
因为是实现双向绑定,在父页面不需要绑定额外的事件
-
但是像单选框、复选框等类型的输入控件可能会将 value 用于不同的目的
-
组件中使用model 选项来避免这样的冲突
-
下面的(第一种方法)和(第二种方法)用于举例使用
-
多层级组件传值,可以在中间层级组件绑定emit
-
<router-view v-bind="$attrs"/> - 父组件到子组件
例子(组件传值的方法还有很多,这两个是使用最普遍的,所以只列举props和emit)
// 组件的js部分,html(vue)部分和一般vue页面相同
export default {
name: 'components-tab',
// 用来实现双向绑定(第二种方法)
model: {
prop: 'checked',
event: 'change'
},
// 子组件需要接收的参数
props:{
// 对应model中的prop值名
checked: Boolean,// (第二种方法)
dataList:{
type: Array,
default: function () {
return [];
},
required: true
},
changeType:{
type:String,
default:'scree'
},
activeItem:{
type:Object,
default:function () {
return {};
}
},
// 在父组件使用v-model传递的,在子组件可以使用value接收(第一种方法)
value: {
type:String,
default:''
}
},
// 需要改变的要定义,不需要改变的可以直接使用props的值
data() {
return {
langTabData:this.dataList,
curActiveItem:this.activeItem,
tabBtn:false,
// 当前选中的item的index
activeIndex:0
};
},
methods: {
// 在父组件使用v-model传递的,在子组件可以使用input事件传出(第一种方法)
changeBtn() {
this.$emit('input', this.activeIndex)
},
// 对应model中的event值名,作用和input相同(第二种方法)
changeBtn1() {
this.$emit('change', this.activeIndex)
}
}
};
// 在页面中使用组件
<template>
<div class="fd-page-box">
<lang-tab class="fd-tab-box"
:dataList="dataList"
:activeItem="activeItem"
@click="clickTab"></lang-tab>
<span v-text="activeItem.key"></span>
</div>
</template>
import tab from "./components/common/tab/index.vue";
export default {
name: 'page-tab',
components: {tab},
data() {
return {
dataList:[{
label:'申请延长解回信息1',
key:'sqycjhxx1'
}, {
label:'申请延长解回信息2',
key:'sqycjhxx2'
}],
activeItem:{
label:'申请延长解回信息5',
key:'sqycjhxx5'
}
}
},
methods: {
clickTab(item){
this.activeItem = item;
}
}
};
Vue Tips:
- 只有data中的数据才是响应式的,动态添加的是非响应式的
- 解决方法:常用$set()、添加多个属性使用Object.assign()
<script>
export default {
name:'app',
data() {
return {
obj:{
name:'喜羊羊'
}
}
},
mounted() {
obj.age = '18'; // 后面不可以动态响应
// 使用以下两种方式
// 推荐使用
this.$set(this.obj,'age','18');
// 添加多个属性是使用
this.obj = Object.assign({}, vm.stu, { age: '18' });
}
}
</script>
- $nextTick怎么使用
- vue是异步更新DOM,去除重复数据,避免不必要的计算和重复DOM操作,所以要拿到更新DOM后的数据需要使用$nextTick
methods: {
getInnerText() {
this.msg = 'change';
this.$nextTick(() =>{
console.log('$nextTick中打印:', this.$el.children[0].innerText);
})
console.log('直接打印:', this.$el.children[0].innerText);
}
}
- vue是单向数据流,数据总是从父组件传到子组件,子组件不能修改父组件传过来的值,只能请求父组件对原始值进行修改
- 在子组件中使用v-model绑定父组件传过来的props值是不规范的会报警告
- 如果需要改变父组件props的值,需要在data里面定义一个变量,初始值为props的值
- 如果频繁的切换使用v-show;如果在运行时条件很少改变使用v-if
- v-if和v-for不建议一起使用
- vue2.0解析时先解析v-for再解析v-if
- 哪怕我们只渲染出一小部分满足条件的元素,也得在每次重渲染的时候遍历整个列表,不论满足条件的数据是否发生了变化
数据变化重渲染的时候遍历整个列表,不论满足条件的数据是否变化: rander函数执行以后会生成vnode(虚拟dom),当数据发生变化时,会触发watcher执行update方法,会重新执行render方法生成新的vnode,所以需要重新遍历数据 在vue2中,为了优化性能,将watcher的粒度放大,变为一个组件一个watcher,所以数据变化值呢通知组件,至于组件的哪个数据变化,应该更新哪个节点,需要和之前生成的vnode进行对比(使用diff算法)