-
Object.assign()
方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。
语法
Object.assign(target, ...sources)
具体请参考:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
-
数组的空位
数组的空位指,数组的某一个位置没有任何值。比如,Array构造函数返回的数组都是空位。
Array(3) // [, , ,]
上面代码中,Array(3)返回一个具有3个空位的数组。
注意,空位不是undefined,一个位置的值等于undefined,依然是有值的。空位是没有任何值,in运算符可以说明这一点。
0 in [undefined, undefined, undefined] // true
0 in [, , ,] // false
上面代码说明,第一个数组的0号位置是有值的,第二个数组的0号位置没有值。
参考:http://es6.ruanyifeng.com/#docs/array
-
fastclick
和better-scroll
点击冲突,只需要给被点击的元素添加一个类:class="needsclick"
, 最新版本的better-scroll
已经解决了这个问题,不用再加class="needsclick"
。
如:给img标签添加class="needsclick"
<slider>
<div v-for="item in recommends">
<a :href="item.linkUrl">
<I mg class="needsclick" @load="loadImage" :src="item.picUrl">
</a>
</div>
</slider>
-
数组根据标题首字母排序
ret.sort((a, b) => {
return a.title.charCodeAt(0) - b.title.charCodeAt(0)
})
参考:http://www.w3school.com.cn/jsref/jsref_charCodeAt.asp
以及:music 歌手页面
-
通过
map
获取一个新的集合数组
computed: {
shortcutList() {
//group:获取到的每一个对象
return this.data.map((group) => {
//从0开始,截取一个字符
return group.title.substr(0, 1)
})
}
},
-
数组过滤 filter()
computed: {
/* 计算出推荐的数组 */
positives() {
return this.ratings.filter((rating) => {
return rating.rateType === POSITIVE;
});
},
/* 计算出吐槽的数组 */
negatives() {
return this.ratings.filter((rating) => {
return rating.rateType === NEGATIVE;
});
}
},
-
@touch 触碰事件
<div class="list-shortcut" @touchstart.stop.prevent="onShortcutTouchStart" @touchmove.stop.prevent="onShortcutTouchMove"
@touchend.stop>
<ul>
<li v-for="(item, index) in shortcutList" :data-index="index" class="item"
:class="{'current':currentIndex===index}">{{item}}
</li>
</ul>
</div>
-
event.target 事件属性可返回事件的目标节点(触发该事件的节点),如生成事件的元素、文档或窗口。
下面的例子可获得触发事件的元素:
<html>
<head>
<script type="text/javascript">
function getEventTrigger(event)
{
x=event.target;
alert("The id of the triggered element: "
+ x.id);
}
</script>
</head>
<body >
<p id="p1" onmousedown="getEventTrigger(event)">
Click on this paragraph. An alert box will
show which element triggered the event.</p>
</body>
</html>
-
Vue.js 定义一个变量,若不想被系统添加set和get方法,可以定义在
created()
钩子里面
这样,系统就不会去观测该变量值的改变,从而影响双向绑定
created() {
this.touch = {}
......
}
-
better-scroll
scrollToElement(), 第一个参数指的是节点,第二个参数:动画时间
下面的例子中,0指的是动画时间为0
这个默认把你所在的那一行滚动到顶部位置!!!
this.$refs.listview.scrollToElement(this.$refs.listGroup[index], 0)
-
methods 方法书写规范
公共方法放在上面,
私有的方法放在下面.
如下图:
methods: {
refresh() {
......
},
scroll(pos) {
......
},
_calculateHeight() {
......
},
_scrollTo(index) {
......
}
},
-
数据的变化,到dom的变化,大概耗时17ms
所以,一般倒计时20ms
后执行需要操作的函数。
watch: {
data() {
setTimeout(() => {
this._calculateHeight()
}, 20)
}
}
-
better-scroll
的probeType属性默认是1,如果想要在scroll快速滚动的时候,正确的监听scroll,需把该属性设置为3。 -
设置background-image:url( ),最好写在行内样式里面,不要写在css里.
<div class="bg-image" :style="bgStyle" ref="bgImage">
计算属性:
computed: {
bgStyle() {
return `background-image:url(${this.bgImage})`
}
},
-
this.$refs.list.$el.style.top =
${this.imageHeight}px
vm.$el : Vue 实例使用的根 DOM 元素, 类型:HTMLElement
-
Math.max(x...) 返回两个指定的数中带有较大的值的那个数。
Math.PI * 2r : 获取圆的周长
-
js修改样式时,则需要把
webkit-xxx
也写上,也可以单独封装来实现autoprefix
效果,如: vue-music注意: width,height 除外
this.$refs.filter.style['backdrop-filter'] = `blur(${blur}px)`
this.$refs.filter.style['webkitBackdrop-filter'] = `blur(${blur}px)`
封装, 创建一个js文件
let elementStyle = document.createElement('div').style
let vendor = (() => {
let transformNames = {
webkit: 'webkitTransform',
Moz: 'MozTransform', // 火狐
O: 'OTransform', // 欧朋
ms: 'msTransform', // IE
standard: 'transform'
}
for (let key in transformNames) {
if (elementStyle[transformNames[key]] !== undefined) {
return key
}
}
return false
})()
// 在外边调用该方法
export function prefixStyle(style) {
if (vendor === false) {
return false
}
if (vendor === 'standard') {
return style
}
// vendor 浏览器厂家
// style.charAt(0).toUpperCase() + style.substr(1) 首字母大写
return vendor + style.charAt(0).toUpperCase() + style.substr(1)
}
在外边直接调用prefixStyle()函数
-
vue-router 按钮返回上一个界面
this.$router.back()
-
push到子路由
this.$router.push({
path: `/singer/${singer.id}` // ${singer.id} 为传递的参数
})
-
Vuex 在传递参数时的使用
- 先在父界面,引入mapMutations 语法糖
import {mapMutations} from 'vuex'
- 在该界面methods中代理该
...mapMutations
语法糖methods: { ...mapMutations({ setSinger: 'SET_SINGER' }) }
- push界面时,调用上面的
setSinger
this.setSinger(singer)
- 在使用该参数的界面,先引入mapGetters语法糖
import {mapGetters} from 'vuex'
- 在计算属性中实现...mapGetters()语法糖
computed: { ...mapGetters([ 'singer' ]) }
- 最后就可以愉快的使用了
this.singer.id 或者 singer.id 看情况决定是否使用this调用
- 先在父界面,引入mapMutations 语法糖
-
创建model 和 class的区别
- 创建model
在外部使用先引入export const playMode = { sequence: 0, loop: 1, random: 2 }
再调用import {playMode} from 'common/js/config'
mode: playMode.sequence
- 创建class
class在外部调用时,需要new一个对象export default class Singer { constructor({id, name}) { this.id = id this.name = name this.avatar = `https://y.gtimg.cn/music/photo_new/T001R300x300M000${id}.jpg?max_age=2592000` } }
map.hot.items.push(new Singer({ name: item.Fsinger_name, id: item.Fsinger_mid }))
- 创建model
-
事件派发
this.$emit('select', item, index)
-
index : index ,前后一致,可以简写为index,如下面的方法
selectItem(item, index) {
this.selectPlay({
list: this.songs,
index
})
},
-
获取屏幕的宽度/高度
window.innerWidth
/window.innerHeight
window.innerWidth / window.innerHeight
-
ES6的写法
const {x, y, scale} = this._getPosAndScale()
_getPosAndScale()函数为
_getPosAndScale() {
const targetWidth = 40
const paddingLeft = 40
const paddingBottom = 30
const paddingTop = 80
const width = window.innerWidth * 0.8
const scale = targetWidth / width
const x = -(window.innerWidth / 2 - paddingLeft)
const y = window.innerHeight - paddingTop - width / 2 - paddingBottom
return {
x,
y,
scale
}
}
-
Vue 过渡动画
transition
,可以利用钩子实现一些特殊动画.
动画和过渡的区别 ?
动画:可以不需要触发就可以执行
过渡:需要人为触发
可以学习vue-music
音乐内核动画
https://cn.vuejs.org/v2/api/#transition
-
create-keyframe-animation 获取dom的fame,用于实现移动动画,利用Vue.js 之 transition的钩子,可以实现各种复杂的动画.
-
x | 0 代表向下取整
const interval = interval | 0 //代表向下取整
-
获取拖动等手势 touchstart, touchmove ,touchend
<div class="progress-btn-wrapper" ref="progressBtn"
@touchstart.prevent="progressTouchStart"
@touchmove.prevent="progressTouchMove"
@touchend="progressTouchEnd"
>
实现方法
created() {
this.touch = {}
},
methods: {
progressTouchStart(e) {
this.touch.initiated = true
// e.touches[0] 表示第 0 个手指的位置
this.touch.startX = e.touches[0].pageX
this.touch.left = this.$refs.progress.clientWidth
},
progressTouchMove(e) {
if (!this.touch.initiated) {
return
}
const deltaX = e.touches[0].pageX - this.touch.startX
const offsetWidth = Math.min(this.$refs.progressBar.clientWidth - progressBtnWidth, Math.max(0, this.touch.left + deltaX))
this._offset(offsetWidth)
},
progressTouchEnd() {
this.touch.initiated = false
this._triggerPercent()
},
_triggerPercent() {
const barWidth = this.$refs.progressBar.clientWidth - progressBtnWidth
const percent = this.$refs.progress.clientWidth / barWidth
this.$emit('percentChange', percent)
},
-
通过点击事件,获取位置
<div class="progress-bar" ref="progressBar" @click="progressClick">
实现方法 e.pageX
获取x
位置
getBoundingClientRect()用于获取某个元素相对于视窗的位置集合。集合中有top, right, bottom, left等属性。 参考:http://www.cnblogs.com/Songyc/p/4458570.html
progressClick(e) {
const rect = this.$refs.progressBar.getBoundingClientRect()
const offsetWidth = e.pageX - rect.left
this._offset(offsetWidth)
// 这里当我们点击 progressBtn 的时候,e.offsetX 获取不对
// this._offset(e.offsetX)
this._triggerPercent()
},
-
圆形进度条的实现
SVG <circle>
-
CSS之stroke: 描边的宽度
-
洗牌函数:用于把数组的顺序打乱,如:随机播放音乐等
slice() 方法可从已有的数组中返回选定的元素。http://www.runoob.com/jsref/jsref-slice-array.html
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1) + min)
}
// 洗牌函数
export function shuffle(arr) {
let _arr = arr.slice() // 类似于拷贝一个副本,如不这样做,会改变原来(arr)数组的顺序,发生bug
for (let i = 0; i < _arr.length; i++) {
let j = getRandomInt(0, i)
let t = _arr[i]
_arr[i] = _arr[j]
_arr[j] = t
}
return _arr
}
-
(ES6) array.findIndex(),用于筛选满足某个条件的item所对应的索引;
let index = list.findIndex((item) => {
// 筛选出当前歌曲id为this.currentSong.id 的item的索引
return item.id === this.currentSong.id
})
-
单曲循环播放原理:当这首歌播放完毕的时候,把他的开始时间重设为0即可
end() {
if (this.mode === playMode.loop) {
//单曲循环播放
this.loop()
} else {
this.next()
}
},
loop() {
this.$refs.audio.currentTime = 0
this.$refs.audio.play()
if (this.currentLyric) {
this.currentLyric.seek(0)
}
},
next() {
if (!this.songReady) {
return
}
if (this.playlist.length === 1) {
this.loop()
} else {
let index = this.currentIndex + 1
if (index === this.playlist.length) {
index = 0
}
this.setCurrentIndex(index)
if (!this.playing) {
this.togglePlaying()
}
}
this.songReady = false
},
-
$el的作用:Vue 实例上的 $el 属性才是 DOM
this.$refs.list.$el.style.top = `${this.imageHeight}px`
和
this.$refs.layer.style[transform] = `translate3d(0,${translateY}px,0)`
的区别
我们先看一下标签内容
第一个是一个自定义组件,也就是一个Vue实例.
<scroll ref="list">
......
</scroll>
第二个是直接是一个标签
<div class="bg-layer" ref="layer"></div>
也就是说:
一个直接是 DOM,一个是 Vue 的实例,Vue 实例上的 $el 属性才是 DOM。
-
获取当前的时间戳
+new Date()
-
res.json() : 转换其他的值到json
下面的例子中:res.json(response.data)
把获取到的数据response.data
转化为json赋值给res
,是该函数可以在外边调用,获取到json的数据.
apiRoutes.get('/getDiscList', function (req, res) {
var url = 'https://c.y.qq.com/splcloud/fcgi-bin/fcg_get_diss_by_tag.fcg'
axios.get(url, {
headers: {
referer: 'https://c.y.qq.com/',
host: 'c.y.qq.com'
},
params: req.query
}).then((response) => {
res.json(response.data)
}).catch((e) => {
console.log(e)
})
})
-
如果更改了后端的代码.node 必须重启服务,才能正常显示
下面webpack里面的内容更改,也需要重启服务器.
-
scroll 中下面两个方法参数的传递
详见vue-music-chapter6
中scroll.vue
组件中的注释
scrollTo() {
// 滚动到某个位置 时间ms 如:({0, 0}, 1000) 滚动到顶部 1000ms
this.scroll && this.scroll.scrollTo.apply(this.scroll, arguments)
},
scrollToElement() {
// 要滚动到的元素 时间ms
this.scroll && this.scroll.scrollToElement.apply(this.scroll, arguments)
}
-
mixin.js 用于写一些方法,合并到调用他的组件的方法中,类似于git提交代码>>合并代码
mixin.js文件中实现下面方法
import {mapGetters} from 'vuex'
export const playlistMixin = {
computed: {
...mapGetters([
'playlist'
])
},
mounted() {
this.handlePlaylist(this.playlist)
},
// 导航条切换会触发此事件
activated() {
this.handlePlaylist(this.playlist)
},
watch: {
playlist(newVal) {
this.handlePlaylist(newVal)
}
},
methods: {
handlePlaylist() {
throw new Error('component must implement handlePlaylist method')
}
}
}
在music-list.vue文件中调用
<script type="text/ecmascript-6">
......
import {playlistMixin} from 'common/js/mixin'
export default {
// mixins是一个数组
mixins: [playlistMixin],
......
},
//在methods中显现该方法:handlePlaylist
methods: {
handlePlaylist(playlist) {
const bottom = playlist.length > 0 ? '60px' : ''
this.$refs.list.$el.style.bottom = bottom
this.$refs.list.refresh()
}
}
</script>
详细使用请参考vue-music
-
在子路由界面按F5刷新数据,由于有一些参数是上一级界面传过来的,会导致刷新获取不到数据,此时可以判断从上一个界面的参数存不存在,不存在就返回到上一级界面
methods: {
_getSongList() {
if (!this.disc.dissid) {
this.$router.push('/recommend')
return
}
}
}
-
loading组件,最好用div再包裹一层,如下面:
<div class="loading-container" v-show="!topList.length">
<loading></loading>
</div>
-
绝对定位设置居中,如垂直居中设置
top: 50%
,transform: translateY(-50%)
,水平居中同理可证;
.list-shortcut
position: absolute
z-index: 30
right: 0
top: 50%
transform: translateY(-50%)
-
设置垂直水平居中,设置
top: 50%
,left: 50%
,transform: translate(-50%, -50%)
position: absolute
top: 50%
left: 50%
transform: translate(-50%, -50%)
z-index: 999
-
this.$watch() 和watch钩子的区别:
created() {
this.$watch('query', debounce((newQuery) => {
this.$emit('query', newQuery)
}, 200))
}
-
数组的截取 slice()
array.slice(0, 10) // 截取数组的前十个;
-
父组件如何调用子组件的方法?
- 子组件
search-box.vue
组件中,有一个方法setQuery()
,想在父组件中调用.methods: { ...... setQuery(query) { this.query = query }, ...... }
- 父组件先添加
ref
属性ref="searchBox"
,在methods钩子中的addQuery()
函数使用this.$refs.searchBox.setQuery()
调用; 具体使用如下:<template> <div class="search"> <div class="search-box-wrapper"> <search-box ref="searchBox" @query="onQueryChange"></search-box> </div> </div> </template> methods: { addQuery(query) { this.$refs.searchBox.setQuery(query) } }
- 子组件
-
对象的扩展运算符 (...)
扩展运算符(...)用于取出参数对象的所有可遍历属性,拷贝到当前对象之中。
let z = { a: 3, b: 4 };
let n = { ...z };
n // { a: 3, b: 4 }
这等同于使用Object.assign方法。
let aClone = { ...a };
// 等同于
let aClone = Object.assign({}, a);
上面的例子只是拷贝了对象实例的属性
... 还有更多功能,请参考:http://es6.ruanyifeng.com/?search=%25E5%25B1%2595%25E5%25BC%2580%25E8%25BF%2590%25E7%25AE%2597%25E7%25AC%25A6&x=0&y=0#docs/object#%E5%AF%B9%E8%B1%A1%E7%9A%84%E6%89%A9%E5%B1%95%E8%BF%90%E7%AE%97%E7%AC%A6
-
javascript 中 push和concat的区别
在javascript中,我们一般都只用push向数组的尾部插入新元素的,但是其实在javascript中还有另外一个方法和push一样,也是向数组尾部插入新元素的,但是他们之间却存在着一定的区别,当我们看下面的代码的时候就明显的知道了:
-
通过使用push操作数组:
- 通过使用concat操作数组:
从上面的两个操作就很明显的看出来push和concat的区别了
①. push 遇到数组参数时,把整个数组参数作为一个元素;而 concat 则是拆开数组参数,一个元素一个元素地加进去。
②.push 直接改变当前数组;concat 不改变当前数组。
-
apply()法的使用
请参考:http://www.cnblogs.com/delin/archive/2010/06/17/1759695.html
-
搜索页面:输入框快速输入(删除)内容 会导致频繁请求数据, 故做截留处理 debounce():自己封装的函数
请参考:search-box.vue 组件
-
让输入框失去焦点blur()
<input ref="query" v-model="query" class="box" :placeholder="placeholder"/>
js调用,让键盘下去,让输入框失去焦点
可以在scroll滚动,或者点击方法中调用
this.$refs.query.blur() // 让移动端输入框失去焦点
-
localStorage 和 sessionStorage 的区别
- localStorage - 没有时间限制的数据存储。
- sessionStorage - 针对一个 session 的数据存储(关闭窗口,存储的数据清空) 。
将数据存到 session中,管你前进后退还是刷新,数据依然在,关闭窗口后再进页面才会清空数据。
-
array.findIndex()
语法: array.findIndex(function())
function() 一般是比较条件
var ages = [3, 10, 18, 20];
function checkAdult(age) {
return age >= 18;
}
function myFunction() {
document.getElementById("demo").innerHTML = ages.findIndex(checkAdult);
}
或者
let index = list.findIndex((item) => {
return item.id === this.currentSong.id
})
-
better-scroll 使用注意:
下面的写法,scroll的高度获取只能是第一个div的高度,这样是不对的!!!
<scroll>
<div></div>
<div></div>
</scroll>
正确的写法:需要用容器包括下
<scroll>
<div class="scroll-wrapper">
<div></div>
<div></div>
</div>
</scroll>
div1和div2里面放的不同的数据数组,如果想让内容撑开scroll,在计算属性中, 把连个div的数据数组concat在一起,然后给scroll的:data属性赋值即可!
为什么要用计算属性呢? 因为在计算属性中,如果两个div的数据数组有一个发生改变,那么计算属性就会重新计算.故保证了数据的正确性!
-
阻止事件冒泡
下面的代码,当我们点击wrapper
里面的任意一个div
时,div
的事件都会冒泡到最外面的wrapper
上面,从而触发wrapper
的点击事件儿.
<div class="wrapper" @click="点击事件儿">
<div></div>
<div></div>
</div>
此时要想阻止里面的div
事件冒泡到最外层的wrapper
上,可以给div
添加@click.stop
阻止事件冒泡即可!
<div class="wrapper" @click="点击事件儿">
<div @click.stop></div>
<div @click.stop></div>
</div>
-
<transition> 和 <transition-group> 的区别?
<transition> 元素作为单个元素/组件的过渡效果。
<transition> 只会把过渡效果应用到其包裹的内容上,而不会额外渲染 DOM 元素,也不会出现在检测过的组件层级中。
<transition-group> 元素作为多个元素/组件的过渡效果。
<transition-group> 渲染一个真实的 DOM 元素。默认渲染 <span>,可以通过 tag 属性配置哪个元素应该被渲染。
transition-group的使用注意事项:
①.需指定tag,如:tag="ul"
;
②.子元素需设置 :key
, 如:<li :key="item.id"></li>
<scroll ref="listContent" :data="sequenceList" class="list-content" >
<transition-group ref="list" name="list" tag="ul">
<li :key="item.id" class="item" v-for="(item,index) in sequenceList"
@click="selectItem(item,index)">
......
</li>
</transition-group>
</scroll>
-
scroll里面有动画时,scroll的refresh()应延时,比如
transition-group
的动画时间是0.1s,那么scroll的refresh()最少延时120ms,才能正确计算高度
:refreshDelay="120" 延时120ms,为什么呢?
因为:transition-group动画时长是100ms, 当从添加歌曲到队列中时,一般情况下,20ms就会执行scroll.refresh()操作,此时动画还没有执行完,会导致scroll的高度计算不对,故 应在动画结束之后再执行refresh()操作,所以延时120ms执行refresh()。
<scroll ref="listContent" :data="sequenceList" class="list-content" :refreshDelay="refreshDelay">
<transition-group ref="list" name="list" tag="ul">
<li :key="item.id" class="item" v-for="(item,index) in sequenceList"
......
</li>
</transition-group>
</scroll>
-
js如何查询某个对象是否在这个数组中?
isFavorite(song) {
const index = this.favoriteList.findIndex((item) => {
return item.id === song.id
})
return index > -1 // 如果大于-1, 说明存在
}
-
打出的包比较大,可以使用路由懒加载优化下 : 这样首屏加载的资源就会很少,提交加载速度!!!!
https://router.vuejs.org/zh-cn/advanced/lazy-loading.html
-
在对Vue.js进行版本升级时,
vue
和vue-template-compiler
的版本号必须一致,否则编译报错!!!
"dependencies": {
"vuex": "^2.3.1", // 必须一致
......
},
"devDependencies": {
"vue-template-compiler": "^2.3.3", // 必须一致
}
-
vConsole 一个轻量、可拓展、针对手机网页的前端开发者调试面板。
在main.js
中调用 ,然后console.log
的内容在手机上就可以查看了.
在上线的时候,把import vConsole from 'vconsole'
注释掉就可以了
// 规避Eslint语法检查, vue中,如果之引入,不调用的话,会报错
/* eslint-disable no-unused-vars */
import vConsole from 'vconsole'