使用 vue 开发过程中遇到的问题或知识点总结,持续更新中…
最近更新:2019-11-29
1.国际化
国际化插件:vue-i18n
2.强制换行与禁止换行
让多行内容显示一行,多余的用...表示
white-space : nowrap
overflow: hidden
text-overflow : ellipsis
内容超过宽度时强制换行
overflow: hidden;
word-wrap:break-word;
overflow-wrap: break-word;
注:CSS3中将 <' word-wrap '> 改名为 <' overflow-wrap '>,用时最好两个都写上
3.显示宽高相等的图片,宽度为屏幕宽度,高度与宽度相等
<div class="image-header">
<img :src="food.image"/>
</div>
.image-header
position: relative
width:100%
height: 0
padding-top : 100%
img
position: absolute
left: 0
top: 0
width: 100%
height: 100%
重点是父元素的height
设为0,padding-top
设为100%
4.转换时间的工具类
/**
* Created by solo on 2018/6/6.
*/
export function formatDatetime(date, fmt) {
if(/(y+)/.test(fmt)){
fmt = fmt.replace(RegExp.$1, (date.getFullYear()+"").substr(4-RegExp.$1.length))
}
let obj = {
"M+": date.getMonth() + 1,
"d+": date.getDay(),
"h+": date.getHours(),
"m+": date.getMinutes(),
"s+": date.getSeconds()
}
for(let key in obj){
if(new RegExp(`(${key})`).test(fmt)){
let str = obj[key] + ''
fmt = fmt.replace(RegExp.$1, RegExp.$1.length === 1 ? str : padLeftZero(str))
}
}
return fmt
}
function padLeftZero(str) {
return ("00" + str).substr(str.length)
}
使用
let date = new Date(timestamp)
let fmtDate = formatDatetime(date, 'yyyy-MM-dd hh:mm')
也可以使用第三方的库: moment.js
、dayjs
5.给组件绑定原生事件
<custom @click.native='handleClick'></custom>
只需要在@click
后面加上.native
就可以直接处理原生点击事件了
6. vue中组件间传值
6.1 父子组件间传值
- 父组件给子组件传值,直接通过
props
传值
<custom content="hello world"></custom>
- 子组件给父组件传值,通过
emit
发送事件
this.$emit('chooseType', type)
父组件接收事件:
<custom content="hello world" @chooseType="handleType"></custom>
6.2 非父子组件传值
主要通过事件总线传值
在根节点给 Vue
挂载一个空的 Vue
对象
Vue.prototype.bus = new Vue();
需要发送事件的组件里
this.bus.$emit("change", params)
接收事件的组件
this.bus.$on("change", (msg) => {
//do yourself work
})
7. 动态组件
动态切换显示的组件
<component :is='type'></component>
data(){
components:{
component-one,
component-two
}
return{
type: 'component-one'
}
}
<component>
是vue官方提供的标签,通过更改 is
指向的子组件名来动态切换组件。
8. v-once 指令
只渲染元素和组件一次。随后的重新渲染,元素/组件及其所有的子节点将被视为静态内容并跳过。这可以用于优化更新性能。
<!-- 单个元素 -->
<span v-once>This will never change: {{msg}}</span>
<!-- 有子元素 -->
<div v-once>
<h1>comment</h1>
<p>{{msg}}</p>
</div>
<!-- 组件 -->
<my-component v-once :comment="msg"></my-component>
<!-- `v-for` 指令-->
<ul>
<li v-for="i in list" v-once>{{i}}</li>
</ul>
9.过渡和动画
9.1 过渡
.fade-enter-active, .fade-leave-active{
transition: opacity 2s
}
.fade-enter, .fade-leave-to{
opacity: 0
}
9.2 动画结合 Animate.css
//引入 animate.css
<link rel="stylesheet" type="text/css" href="animate.css">
//布局
<transition enter-active-class="animated bounce" leave-active-class="animated shake">
<p v-if="show">hello world</p>
</transition>
<button @click='toggleShow'>toggle</button>
要定义 enter-active-class
和 leave-active-class
的类名,且必须有 animated
,想要什么动画效果就写在第二个位置上
解决第一次显示没有动画的bug
<transition
appear
enter-active-class="animated bounce"
leave-active-class="animated shake"
appear-active-class="animated bounce">
<p v-if="show">hello world</p>
</transition>
在 <transition>
上添加 appear
和 appear-active-class
即可。
9.3 同时使用过渡和动画
<transition
name="fade"
type='transition'
appear
enter-active-class="animated bounce fade-enter-active"
leave-active-class="animated shake fade-leave-active"
appear-active-class="animated bounce">
<p v-if="show">hello world</p>
</transition>
在 enter-active-class
和 leave-active-class
加上相应的类名 fade-enter-active
和 fade-leave-active
,然后在样式中定义过渡效果即可。
.fade-enter-active, .fade-leave-active{
transition: opacity 2s
}
.fade-enter, .fade-leave-to{
opacity: 0
}
动画执行的总时长是根据动画还是过渡来定呢?可以手动指定:
//指定整体动画时间为过渡动画时间
type='transition'
还可以自己指定动画总时长:
//指定动画时长为10秒
:duration="10000"
//分别指定进场时长5秒和出场动画时长10秒
:duration="{enter: 5000, leave: 10000}"
9.4 多个组件和元素的过渡
- 多个元素过渡
<div id="app">
<transition name="fade" mode="out-in">
<div v-if="show" key="hello">Hello world</div>
<div v-else key="bye">Bye world</div>
</transition>
<button @click="toggleShow">Add</button>
</div>
需要给元素加 key
, 防止vue复用元素导致没有动画效果。
可以指定切换模式,mode="out-in"
:先出后进,mode="in-out"
:先进后出
- 多个组件过渡跟多个元素过渡类似
9.5 vue中列表过渡
使用 transition-group
属性
<div id="app">
<transition-group name="fade">
<div v-for="item in list" :key="item.id">
{{item.title}}
</div>
</transition-group>
<button @click="add2List">Add</button>
</div>
<style type="text/css" >
.fade-enter-active, .fade-leave-active{
transition: opacity 2s
}
.fade-enter, .fade-leave-to{
opacity: 0
}
</style>
10. img 标签的 src 动态绑定
1)路径固定的图片
路径前加 require()
<img :src="bookingManageImg" slot="icon"/>
bookingManageImg(){
return this.selectedTab === "bookingManage" ? require('../assets/manage_focus.png') : require('../assets/manage_normal.png')
},
2)for 循环里图片的路径不固定
如果在循环里还直接用 require()
的话,webpack 会将图片来当做模块来用,因为是动态加载的,所以 url-loader 将无法解析图片地址,所以会报错找不到模块。
解决办法是采用拼接的方式: require('../assets/icons/' + item.icon + '.png')
,对象里只存图片的名字,图片路径是固定的,所以直接用字符串写上去。
list 的数据格式:
const list = [
{
name: "美食",
icon: "food"
},
{
name: "电影",
icon: "movie"
},
]
布局文件:
<div class="item" v-for="item in list">
<img :src="require('../assets/icons/' + item.icon + '.png')" class="icon">
<div class="name">{{item.name}}</div>
</div>
11. vuex 在页面刷新后状态丢失解决办法
刷新页面后,存在 vuex 的数据会丢失,给调试带来不便。把用户的登录信息放到 sessionStorage
中避免丢失。
const USER_INFO = "userInfo";
export default new Vuex.Store({
state: {
userInfo: JSON.parse(sessionStorage.getItem(USER_INFO))
},
mutations: {
setUserInfo(state, userInfo){
//存储到 sessionStorage 中以防刷新页面后状态丢失
sessionStorage.setItem(USER_INFO, JSON.stringify(userInfo));
state.userInfo = userInfo
}
}
}
12. 返回记住滚动条位置
详细解析见文章:Vue 返回记住滚动条位置详解
13. 修改页面 Title
首先在 router.js
里,每个路由加上 meta
,设置 title
routes: [
{
path: '/login',
name: 'login',
component: Login,
meta:{
title:'登录'
}
},
{
path: '/home',
name: 'home',
component: Home,
children: [],
meta:{
title:'主页'
}
}
]
然后在 main.js
里通过前置路由动态修改 title
router.beforeEach((to, from, next) => {
/* 路由发生变化修改页面title */
if (to.meta.title) {
document.title = to.meta.title;
}
next();
})
14. 打包时启用 Gzip 压缩
先安装 webpack 插件
npm install --save-dev compression-webpack-plugin
再在 vue.config.js
里添加如下代码:
const CompressionPlugin = require("compression-webpack-plugin")
module.exports = {
// 基本路径
baseUrl: './',
// 输出文件目录
outputDir: 'dist',
// 启用 Gzip 压缩
configureWebpack: () => {
module.exports = {
configureWebpack:config=>{
if(progress.env.NODE_ENV === 'production'){
return{
plugins: [
new CompressionPlugin({
test:/\.js$|\.html$|.\css/, //匹配文件名
threshold: 10240,//对超过10k的数据压缩
deleteOriginalAssets: false //不删除源文件
})
]
}
}
},
}
},
}
Vue CLI 3 默认没有 vue.config.js
,在根目录新建一个就好,位置跟 package.json
同级。
15. vue 与 安卓原生应用通信
我有一篇专门讲解vue与安卓双向通信的文章:
16. 如何在样式中使用 scss 的声明的全局变量
sass 声明的变量如:
$color-primary: #409EFF;
$color-success: #67C23A;
$color-warning: #E6A23C;
$color-danger: #F56C6C;
$color-info: #909399;
普通的引用方法为
<style scoped lang="scss">
@import "../../public/css/index";
.home {
color: $color-primary;
}
</style>
需要先在要使用的文件中引入声明的文件,然后才能使用。
这样比较麻烦,代码冗余。可以使用更优雅的方式:sass-resources-loader
使用 sass-resources-loader
需要两步:
-
安装依赖
npm install sass-resources-loader
-
vue.config.js
里配置。这里使用的是 Vue-CLI 3. 将代码中的
resources
路径换成自己的路径即可。// vue.config.js module.exports = { chainWebpack: config => { const oneOfsMap = config.module.rule('scss').oneOfs.store oneOfsMap.forEach(item => { item .use('sass-resources-loader') .loader('sass-resources-loader') .options({ // Provide path to the file with resources resources: './path/to/resources.scss', // Or array of paths resources: ['./path/to/vars.scss', './path/to/mixins.scss'] }) .end() }) } }
其他环境的详细配置说明见 sass-resources-loader 官网
配置完之后,就可以在任意文件里使用 sass 声明的变量啦。
17. 子组件中改变父组件通过 props 传递过来的属性
官方是不推荐子组件直接改变父组件传递过来的属性的,如果你这么做了,会有警告。
但有时的确是需要在子组件中改变父组件的属性,因为省事啊……比如子组件中有 Dialog,Dialog 的显示与隐藏要通过父组件控制,同时子组件关闭了 Dialog 要同步更新父组件中属性值。
当然有很多 "正确" 的方式可以做到,比如 vuex
,比如用父子组件的通信,子组件改变值了发个通知通知父组件更新对应的值。
但是,上面两种方法都比较麻烦。我就想在父组件中给子组件传递个变量,子组件改变它的值了,父组件中的变量也会自动更新。
这就用到一个 "漏洞",把要传递的值封装成一个对象,改变对象中的属性值,就不会出现警告。因为对象还是原来的对象,只是里面的值变了。
父组件如下。注意 data 中的 visible: {value: false}
是个对象,不能写成 visible: false
,会出现警告。
<template>
<child :visible="visible"/>
</template>
<script>
export default {
components: {
child
},
data(){
return{
visible: {value: false}
}
}
}
</script>
子组件如下:
<el-dialog :visible.sync="visible.value">
当子组件改变值时改变的是 visible
对象中的 value
属性。这样就可以通过子组件直接改变父组件的值了。
18. 九宫格的实现
实现类似的九宫格代码:
<template>
<div class="view-home">
<div class="category-wrapper">
<div class="category-name">分类一</div>
<div class="icons">
<div class="item" v-for="item in list">
<img :src="require('../assets/icons/' + item.icon + '.png')" class="icon">
<div class="name">{{item.name}}</div>
</div>
</div>
</div>
</div>
</template>
<script>
const LIST = [
{name: "电影",icon: "movie"},
{name: "美食",icon: "food"},
{name: "美发",icon: "hair"},
{name: "周边游",icon: "around"},
{name: "酒店",icon: "hotel"},
{name: '代购',con: "dg"}
];
export default {
components: {},
data() {
return {
list: ICON_LIST,
}
},
}
</script>
<style scoped lang="scss">
.view-home {
display: flex;
flex-direction: column;
width: 100%;
padding: px2rem(10);
box-sizing: border-box;
.category-wrapper {
width: 100%;
display: flex;
flex-direction: column;
background-color: white;
.category-name {
font-size: $font-size-normal;
color: $text-main;
padding: px2rem(12);
border-bottom: px2rem(1) solid $border-third;
}
.icons {
display: flex;
flex-direction: row;
flex-wrap: wrap;
.item {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 25%;
padding: px2rem(10) 0;
.icon {
width: px2rem(40);
height: px2rem(40);
}
.name {
font-size: $font-size-small;
color: $text-normal;
margin-top: px2rem(10);
}
}
}
}
}
</style>
重点:
-
想要自动换行,通过以下三行代码实现:
display: flex; flex-direction: row; flex-wrap: wrap;
-
每行几个图标,通过 item 的宽度控制。如果每行四个图标,那 item 的宽度就是 25%,如果每行5个图标,那每个 item 的宽度就是 20%.
19. 超出宽度横向滑动
当子组件的宽度超过父组件,实现横向滑动。
父组件可以是整个屏幕的根元素,也可以是某个特定的元素。只要设置好 css 即可。
设父元素的 class=parent
,子元素的 class=child
.parent{
//其他样式省略,只列出控制横向滑动必须的代码
display: flex;
overflow-x: auto;
overflow-y: hidden;
.child{
// 其他样式省略,只列出控制横向滑动必须的代码
//这句话的意思是不会被压缩大小
flex-shrink: 0;
}
}
在你的 css 代码中加上这几行,就可以实现横向滑动啦。
20. 只显示 n 行,多余的用省略号表示
经常有需求是只显示两行或三行,多余的用省略号表示。
适用范围:
因使用了WebKit的CSS扩展属性,该方法适用于WebKit浏览器及移动端;
注:
-webkit-line-clamp用来限制在一个块元素显示的文本的行数。 为了实现该效果,它需要组合其他的WebKit属性。
常见结合属性:
display: -webkit-box: 必须结合的属性 ,将对象作为弹性伸缩盒子模型显示。
-webkit-box-orient: 必须结合的属性,设置或检索伸缩盒对象的子元素的排列方式。
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
21. flex 布局中,单个元素靠右对齐
如下图,姓名、性别、评论图标这一行有三个元素,是 flex 布局,前两个元素靠左,评论图标靠右。
已知父元素的布局为
display: flex;
flex-direction: row;
align-items: center;
实现起来有三种方法:
- 给姓名和性别两个元素再加一层 div, 并把这个 div 设置
flex: 1
。缺点是多了层嵌套,有点麻烦。 - 给评论图标这个元素设置
flex: 1; text-align: right;
- 给评论图标这个元素设置
margin-left: auto;
后两种方法都比较简单,推荐。