用vue写项目有一段时间了,想把项目中学到的一些知识记下来,供以后复习用,这个文章我会一直更新下去的。(ps:本人前端小白,写的都是比较基础的,有什么不对的欢迎指正,大佬勿喷。)
1、深度选择器
当我们使用第三方组件库时,其组件的样式不一定满足我们项目的需求,我们需要修改其组件的样式,但是正常的复制类名更改样式的方法并不能奏效。
原因是其组件的 style 标签设置了 scoped 之后,作用域是封闭的,父组件的样式无法渗透到子组件中。如果希望 scoped 样式中的一个选择器能够作用得“更深”,例如影响子组件这时就需要用到深度选择器。
1、对于 css 语法
使用 >>> 操作符,例:
<style scoped>
.a >>> .b {
/* ... */
}
</style>
它将被编译成:
.a[data-v-f3f3e99] .b { /* ... */ }
2、对于 less、scss 等预处理器
像 Less、Sass 之类的预处理器无法正确解析 >>>,这种情况下可以使用 /deep/ 操作符取而代之(这是 >>> 的别名),例:
.a{
/deep/ .b{
/* ... */
}
}
2、清除定时器
因为 vue 是单页应用,所以在页面中设置的定时器如果不及时清除的话就会一直执行,可以想象一下如果你在这个定时器中调用了接口,那么就会不停的请求,这是非常损耗性能的,所以一定要养成及时清除定时器的好习惯。以下是我知道的在 vue 中清除定时器的方法:
方法1、
data () {
return {
timer: '' // 定时器名字
}
},
created () {
this.timer = setInterval(() => {
/* ... */
}, 1000)
},
beforeDestory () {
clearInterval(this.timer) // 在销毁组件前清除定时器
}
方法1有两个不好的地方,引用尤大的话来说就是:
- 它需要在这个组件实例中保存这个timer,如果可以的话最好只有生命周期钩子可以访问到它。这并不算严重的问题,但是它可以被视为杂物。
- 我们的建立代码独立于我们的清理代码,这使得我们比较难于程序化的清理我们建立的所有东西。
方法2、
created () {
var timer = setInterval(() => {
/* ... */
}, 1000)
// 通过$once来监听定时器,在beforeDestroy钩子可以被清除。
this.$once('hook:beforeDestroy', () => {
clearInterval(timer)
})
}
该方法是通过$once这个事件侦听器在定义完定时器之后的位置来清除定时器。
3、methods、computed、watch
1、methods 和 computed
methods 和 computed 都可以求得一个结果,这是它们的相同点
不同点是
- methods 是每调用一次就会执行一次
- computed 有一个缓存机制,只有当缓存的数据发生了改变才会重新执行,而且 computed 必须有 return 返回值。
template:
<div id="app">
<p>{{ handleSum() }}</p>
<p>{{ sum }}</p>
</div>
script:
computed: {
sum () {
return 1 + 1
}
},
methods: {
handleSum () {
return 1 + 1
}
}
总结:当需要存储一个性能开销很大的结果时,这时候就推荐用 computed 缓存该值;如果需要每次都重新加载并且不需要缓存的值就用 methods
2、computed 和 watch
watch擅长处理的场景:一个数据影响多个数据
computed擅长处理的场景:一个数据受多个数据影响
以下是 watch 的用法
template:
<div id="app">{{ fullName }}</div>
script:
data () {
return {
firstName: "Dell",
lastName: "Lee",
fullName: "Dell Lee"
}
},
watch: {
firstName () { // 监听 firstName属性
this.fullName = this.firstName + " " + this.lastName
},
lastName () { // 监听 lastName属性
this.fullName = this.firstName + " " + this.lastName
}
}
以下是 computed 的用法
template:
<div id="app">{{ fullName }}</div>
script:
data () {
return {
firstName: "Dell",
lastName: "Lee",
fullName: "Dell Lee"
}
},
computed: {
fullName: {
get: function () {
return this.firstName + " " + this.lastName;
},
set: function (value) { // value就是fullName改变后的值
var arr = value.split(" ")
this.firstName = arr[0] // 同时修改firstName
this.lastName = arr[1] // 同时修改lastName
}
}
}
4、 多用全等而不是相等
==相等运算符与 ===全等运算符的概念就不多说了。
问题:项目中碰到的坑就是我在后台编辑某个产品的价格 price 为 0 时,始终无法保存(我是判断 price != '' 为true则保存)。
但 '' 转换为数字类型后结果为 0,所以 0 != '' 结果始终为false,这时就需要用 !==不全等运算符进行判断。
总结:===全等运算符更加的严谨,所以尽量多用 ===全等运算符去判断而不是偷懒用 ==相等运算符。
5、v-for 循环遍历 img标签,动态绑定 src时,无法显示图片问题
原因:系统无法识别 @这种自定义的路径符号
解决:
1、把图片放在 static 文件夹下,用绝对路径引入
2、把图片放在 cdn 上,通过网络路径引入
3、通过 require 引入路径,例:
imgUrl: require("@/assets/images/arrow1.png")
6、.native修饰符
理解:.native修饰符可以使一个组件在根元素上监听原生事件,也就是说它可以把组件变成普通的 HTML标签,主要用于给自定义的组件或者第三方组件添加原生事件,例:
<base-input v-on:focus.native="onFocus"></base-input>
如果不加.native修饰符将无法触发 onFocus事件
7、.sync修饰符
在父子组件的通信中,父组件向子组件传值,子组件可以通过 props 接收,子组件可以修改父组件传入的值,但是会报错,官方推荐以 update:myPropName 的模式触发事件取而代之,例:
父组件:
<div id="app">
<Son :isShow.sync="isShow">
</div>
script:
data () {
return {
isShow: true
}
}
子组件:
<div class="son" v-show="isShow">
<button @click="close" type="button">关闭</button>
</div>
script:
props: {
isShow: {
type: Boolean,
default: true
}
},
methods: {
close () {
this.$emit('update:isShow', false)
}
}
8、父子组件的生命周期
加载渲染过程:
父beforeCreate -> 父created -> 父beforeMount -> 子beforeCreate -> 子created ->
子beforeMount -> 子mounted -> 父mounted
子组件更新过程:
父beforeUpdate -> 子beforeUpdate -> 子updated -> 父updated
父组件更新过程:
父beforeUpdate -> 父updated
销毁过程:
父beforeDestroy -> 子beforeDestroy -> 子destroyed -> 父destroyed
9、长列表的优化
Vue会通过 object.defineProperty 对数据进行劫持,来实现视图响应数据的变化,然而有些数据可能只需要展示并不需要发生响应式的变化,这个时候可以用 Object.freeze
Object.freeze 可以冻结一个对象,被冻结的对象将无法再修改,例:
data () {
return {
users: {}
}
},
async created() {
const users = await axios.get("/api/users")
this.users = Object.freeze(users)
}
需要说明的是,这里只是冻结了 users的值,引用不会被冻结,当我们需要 reactive数据的时候,我们可以重新给 users赋值。
10、$options
vm.$options 用于当前 Vue 实例的初始化选项。可以用来重置 data 对象到初始化的状态
this.$data // 当前的 data对象
this.$options.data() // 获取初始化状态下的 data对象
Object.assign(this.$data, this.$options.data()) // 重置 data 对象到初始化的状态
11、父组件异步传值给子组件
父组件异步获取值再传给子组件,子组件并不能一开始就获取到值,导致有时候会报错。以下为常用的两种方法:
(1)通过 v-if 控制子组件
父组件:
<div id="father">
<Son :name="name" v-if="showChild></Son>
</div>
script:
data () {
return {
name: '',
showChild: false
}
},
async created() {
const name = await axios.get("/api/name")
this.name = name
this.showChild = true
}
(2)子组件使用 watch 来监听父组件改变的 props
父组件:
<div id="father">
<Son :name="name"></Son>
</div>
script:
data () {
return {
name: ''
}
},
async created() {
const name = await axios.get("/api/name")
this.name = name
}
子组件:
<div id="son">
<p class="name">{{ name }}</p>
</div>
script:
props: {
name: {
type: String,
default: ''
}
},
watch: {
name (newValue, oldValue) {
this.name = newValue
}
}
12、异步加载组件
当一个页面使用了大量组件时,从服务器上同时加载所有组件可能是没有意义的,有些组件可能不需要马上加载。在这种情况下,Vue 允许我们在需要时定义从服务器异步加载的组件。
通过仅加载基本组件并把异步组件的加载推迟到未来的调用时间,可以节省带宽和程序加载时间。
这是一个简单的异步加载组件:
new Vue({
components: {
"child": () => import("./components/child")
}
})
13、自定义组件的 v-model
父组件:
<template>
<div class="parent">
父级内容:{{ data }}
<Child v-model="data"></Child>
</div>
</template>
<script>
import Child from './components/child'
export default {
name: 'parent',
data() {
return {
data: 1,
}
},
components: {
child
}
}
</script>
子组件:
<template>
<div>
<p>子级内容:{{ childnum }}</p>
<button @click="changeNum">点我</button>
</div>
</template>
<script>
export default {
name: 'child',
model: {
prop: 'childnum', // 自定义prop属性,默认绑定的是value
event: 'changeNum' // 自定义触发事件类型,默认触发的事件类型是input
},
props: {
childnum: {
type: Number
}
},
methods: {
changeNum() {
this.$emit('changeNum', this.childnum + 1)
}
},
}
</script>
原理:子组件定义 model 属性中的 prop 和 event,父组件通过 v-model 传值给子组件 model 属性中的 prop 对应的变量。然后子组件通过 $emit 发送 event 事件并传递一个结果值,这样外部的 v-model 就收到了传出的值,因此就实现了双向传递。(默认情况下,一个组件上的 v-model 会把 value 用作 prop 且把 input 用作 event)
14、选择性的调用 v-if 和 v-show
- v-if 是对节点进行增加和删除操作,同时它是惰性的,只在条件为 true 时渲染
- v-show 是对 display 样式进行 block 和 none 的切换,无论条件 true 还是 false 它都会渲染
所以,在需要频繁切换显示隐藏,并且不需要权限的元素上使用 v-show,减少系统的切换开销,否则用 v-if
15、vue 配置跨域
如果前端应用和后端 API 服务器没有运行在同一个主机上,需要在开发环境下将 API 请求代理到 API 服务器。通过配置根目录下的 vue.config.js 文件的 devServer.proxy 选项来实现代理
module.exports = {
devServer: {
proxy: {
'/api': {
target: 'http://49.234.33.207:80', // 要代理的域名
changeOrigin: true,
ws: true,
pathRewrite: { // 路径重写
'^/api': ''
}
}
}
},
}
使用:
this.$axios.get("/api/getData")
// /api/getData 相当于 http://49.234.33.207:80/getData
// /api 相当于 http://49.234.33.207:80