vue 相关
1.vue v-for 循环一个数组,key值报错,但是数据是正常显示的
报错:
v-for使用key,需要在key前加上:key;
srcList是个数组,key值绑定不能是数据类型Object的item,应该绑定item对象下一个属性,这个属性不能重复出现,否则依旧会出现key值报错;你这里可以改成v-for="(item,index) in srcList" :key="index",index对象数组里的索引,不会重复出现,也就不会报错
2.计算属性 和 watch 的区别
计算属性是自动监听依赖值的变化,从而动态返回内容,监听是一个过程,在监听的值变化时,可以触发一个回调,并做一些事情
所以区别来源于用法,只是需要动态值,那就用计算属性;需要知道值的改变后执行业务逻辑,才用 watch,用反或混用虽然可行,但都是不正确的用法
watch 用法: 例如有请求需要再也没初始化的时候就执行一次,然后监听他的变化,很多人这么写:
created(){ this.fetchPostList() }, watch: { searchInputValue(){ this.fetchPostList() } }
上面这种写法,我们完全可以如下写:
watch: { searchInputValue:{ handler: 'fetchPostList',
immediate: true } }
immediate:true代表如果在 wacth 里声明了之后,就会立即先去执行里面的handler方法,如果为 false就跟我们以前的效果一样,不会在绑定的时候就执行。
3.keep-alive
简单来说,就是把一个组件的编译缓存起来
4.router 与 route 的区别
$route对象表示当前的路由信息,包含了当前 URL 解析得到的信息。包含当前的路径,参数,query对象等。
// 1.$route.path 字符串,对应当前路由的路径,总是解析为绝对路径,如 "/foo/bar"。 // 2.$route.params 一个 key/value 对象,包含了 动态片段 和 全匹配片段, 如果没有路由参数,就是一个空对象。 // 3.$route.query 一个 key/value 对象,表示 URL 查询参数。 例如,对于路径 /foo?user=1,则有 $route.query.user == 1, 如果没有查询参数,则是个空对象。 // 4.$route.hash 当前路由的 hash 值 (不带 #) ,如果没有 hash 值,则为空字符串。锚点 // 5.$route.fullPath 完成解析后的 URL,包含查询参数和 hash 的完整路径。 // 6.$route.matched** 数组,包含当前匹配的路径中所包含的所有片段所对应的配置参数对象。 // 7.$route.name 当前路径名字** // 8.$route.meta 路由元信息
$router对象是全局路由的实例,是router构造方法的实例。
路由实例方法:
1. $router.push()
// 字符串 this.$router.push('home') // 对象 this.$router.push({ path: 'home' }) // 命名的路由 this.$router.push({ name: 'user', params: { userId: 123 }}) // 带查询参数,变成 /register?plan=123 this.$router.push({ path: 'register', query: { plan: '123' }}) // push方法其实和<router-link :to="...">是等同的。 // 注意:push方法的跳转会向 history 栈添加一个新的记录,当我们点击浏览器的返回按钮时可以看到之前的页面。
2. $router.go()
// 页面路由跳转 前进或者后退 this.$router.go(-1) // 后退
3. $router.replace()
//push方法会向 history 栈添加一个新的记录,而replace方法是替换当前的页面,不会向 history 栈添加一个新的记录 // 一般使用replace来做404页面 this.$router.replace('/') // 配置路由时path有时候会加 '/' 有时候不加,以'/'开头的会被当作根路径,就不会一直嵌套之前的路径。
5.vue base64编码
// 项目根目录下安装 npm install --save js-base64 // 项目文件中引入 let Base64 = require('js-base64').Base64 // base64编码 Base64.encode('dankogai'); // ZGFua29nYWk= // base64解码 Base64.decode('5bCP6aO85by-'); // 小飼弾 // base64加密后的内容,如果放到 url中传输,就会出现空格问题,即经过加密的字符串中如果有‘+’号,就会变成空格 encodeURIComponent(Base64.encode('dankogai'))
6.vue 实现消息的无缝滚动效果
<template> <div id="box" @mouseover="pause" @mouseout="goon"> <ul id="con1" ref="con1" :class="{anim:animate==true}"> <li v-for='item in items'>{{item.name}}</li> </ul> </div> </template> <script> export default { data() { return { animate:false, timer: null, items:[ //消息列表对应的数组 {name:"马云"}, {name:"雷军"}, {name:"王勤"} ] } }, created(){ setInterval(this.scroll,1000) // 在钩子函数中调用我在method 里面写的scroll()方法,注意此处不要忘记加this,我在这个位置掉了好几次坑,都是因为忘记写this。 }, methods: { play () { this.timer = setInterval(this.scroll, 2000) }, pause () { clearInterval(this.timer) }, goon () { this.timer = setInterval(this.scroll, this.val.scrollSpeed * 1000) }, scroll(){ let con1 = this.$refs.con1; con1.style.marginTop='-30px'; this.animate=!this.animate; var that = this; // 在异步函数中会出现this的偏移问题,此处一定要先保存好this的指向 setTimeout(function(){ that.items.push(that.items[0]); that.items.shift(); con1.style.marginTop='0px'; that.animate=!that.animate; // 这个地方如果不把animate 取反会出现消息回滚的现象,此时把ul 元素的过渡属性取消掉就可以完美实现无缝滚动的效果了 },500) } } } </script> <style> *{ margin: 0 ; padding: 0; } #box{ width: 300px; height: 32px; line-height: 30px; overflow: hidden; padding-left: 30px; border: 1px solid black; transition: all 0.5s; } .anim{ transition: all 0.5s; } #con1 li{ list-style: none; line-height: 30px; height: 30px; } </style>
7.vue 项目结构启动原理
vue调用顺序: index.html → main.js → app.vue → index.js → components/组件
一般项目创建好后会有三个文件:index.html、main.js、app.vue
1、index.html :所有vue文件都是单页面形式开发,所有vue组件都是通过index.html进行渲染加载。
2、main.js:相当于
java的入口函数,控制初次启动vue项目要加载的组件
import Vue from 'vue' 引入vue
import App from './App' 引入App.vue文件
import router from './router' 引入一段路由配置
Vue.use(C)全局方法定义为C
el:'#app'这个和index.html中的app组件挂钩。官网解释为:模板将会替换挂载的元素。
watch:用来监听路由的变换,可以用来定义页面切换时的过渡效果。
3、App.vue默认为一个根组件
export 中的name属性组件名字
created:生命周期函数
4、index.js文件
引入组件的代码
routes定义时,path为你以后页面间路由跳转的路径
name为import进来的名字
component也为这个名字
其他文件:
-build
-build.js 生产环境构建脚本
-utils.js 构建相关工具方法
-webpack.base.conf.js webpack基础配置
-webpack.dev.conf.js webpack开发环境配置
-webpack.prod.conf.js 生产环境配置
-confg 项目配置
--dev.env.js 开发环境变量
--index.js 项目配置文件
--prod.env.js 生产环境变量
--test.env.js 测试环境变量
-package.json npm包配置文件,里面定义了项目的npm脚本,依赖包等信息
-src 源码目录
--main.js 入口js文件
--app.vue 根组件
--components 公共组件目录
--title.vue
————————————————
版权声明:本文为CSDN博主「No Silver Bullet」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/sunhuaqiang1/article/details/85099769
8.vue 生命周期相关
生命周期:Vue 实例从开始创建、初始化数据、编译模板、挂载Dom→渲染、更新→渲染、卸载等一系列过程,我们称这是 Vue 的生命周期,各个阶段有相对应的事件钩子
注意:
-
created阶段的ajax请求与mounted请求的区别:前者页面视图未出现,如果请求信息过多,页面会长时间处于白屏状态
-
mounted 不会承诺所有的子组件也都一起被挂载。如果你希望等到整个视图都渲染完毕,可以用 vm.$nextTick
vue2.0之后主动调用$destroy()不会移除dom节点,作者不推荐直接destroy这种做法,如果实在需要这样用可以在这个生命周期钩子中手动移除dom节点
单个组件的生命周期
-
初始化组件时,仅执行了beforeCreate/Created/beforeMount/mounted四个钩子函数
-
当改变data中定义的变量(响应式变量)时,会执行beforeUpdate/updated钩子函数
-
当切换组件(当前组件未缓存)时,会执行beforeDestory/destroyed钩子函数
-
初始化和销毁时的生命钩子函数均只会执行一次,beforeUpdate/updated可多次执行
父子组件的生命周期
//父组件 <script> import Child from './Child'; export default { components: { Child }, beforeCreate() { console.log('parent before create'); }, created() { console.log('parent created'); }, beforeMount() { console.log('parent before mount'); }, mounted() { console.log('parent mounted') }, render(h) { console.log('parent render'); return ( <div> <h1>Vue中父子组件的挂载顺序</h1> <h1>父亲</h1> <Child/> </div> ) }, } </script> // 子组件 <script> export default { beforeCreate() { console.log('child before create'); }, created() { console.log('child created'); }, beforeMount() { console.log('child before mount'); }, mounted() { console.log('child mounted') }, render(h) { console.log('child render'); return ( <div> <h1>Vue中父子组件的挂载顺序</h1> <h1>孩子</h1> </div> ) }, } </script>
父子组件中使用render函数代替<template>,打印输出结果
父组件先初始化 -> 父组件渲染完毕 -> 子组件开始初始化 -> 子组件挂载完毕 -> 父组件挂载完毕
-
仅当子组件完成挂载后,父组件才会挂载
-
当子组件完成挂载后,父组件会主动执行一次beforeUpdate/updated钩子函数(仅首次)
-
父子组件在data变化中是分别监控的,但是在更新props中的数据是关联的(可实践)
-
销毁父组件时,先将子组件销毁后才会销毁父组件
兄弟组件的生命周期
-
组件的初始化(mounted之前)分开进行,挂载是从上到下依次进行
-
当没有数据关联时,兄弟组件之间的更新和销毁是互不关联的
宏mixin的生命周期
- mixin中的生命周期与引入该组件的生命周期是仅仅关联的,且mixin的生命周期优先执行
9. 怎样监听vuex中的数据变化
将vuex中的数据映射成组件中的计算属性
import { mayState } from 'vuex'; . . . computed: { ...mapState([ 'dataName' ]) }
10. 自定义组件的v-model
// 父组件 <template> <div class="login"> <BaseInput :value="message" @input="message = $event"/>
<p>文字: {{message}}</p>
<hr/>
<BaseCheckbox style="width:40px;height:40px;" :checked="checked" @change="checked = $event"></BaseCheckbox>
<p>是否选中: {{checked}}</p> </div> </template> <script>
import BaseInput from "./baseInput" import BaseCheckbox from './baseCheckbox' export default { components: {
BaseInput, BaseCheckbox }, data() { return {
message: 'Hello 输入框', checked: true, } } }; </script>
// 子组件baseCheckbox <template> <div> <input type="checkbox" :checked="checked" @change="handleChange" /> </div> </template> <script> export default { model: { prop: 'checked', event: 'change' }, props: ['checked'], methods: { handleChange(e) { this.$emit('change', e.target.checked); } } }; </script>
// 子组件baseInput <template> <div> <input type="text" :value="value" @input="handleInput" /> </div> </template> <script> export default { props: ['value'], methods: { handleInput(e) { this.$emit('input', e.target.value); } } }; </script>
11. Vue异步组件
在项目当中有些组件不想马上加载,如点击按钮之后再加载组件,可以使用异步组件
<template> <div> <h1>vue异步组件的使用</h1> <button @click="handleClick">点击加载组件</button> <div v-if="show"> <List /> </div> </div> </template> <script> export default { components: { // 使用异步组件,在需要的时候发送ajax请求,下载对应组件的代码 // List: ()=> import('./list') // 为了便于查看,在控制台中设置文件名称为 list List: ()=> import(/* webpackChunkName: list */'./baseCheckbox') }, data() { return { show: false } }, methods: { handleClick() { this.show = !this.show; } } } </script>
或者使用异步组件工厂函数的形式,设置加载属性,如加载时间,延时时间,加载失败后组件等等
<template>
<div>
<h1>vue异步组件的使用</h1> <button @click="handleClick">点击加载组件</button> <div v-if="show"> <AsyncList/> </div> </div> </template> <script> import LoadingComponent from './LoadingComponent';
import ErrorComponent from './ErrorComponent';
const AsyncList = () => {
return {
component: import('./list'),
loading: LoadingComponent,
error: ErrorComponent,
delay: 200,
timeout: 3000
}
} export default { components: { AsyncList }, data() { return { show: false } }, methods: { handleClick() { this.show = !this.show; } } } </script>
12. $attr/$listeners
最外层和最内层需要进行隔一代传参的时候
使用B来做中转站,当A组件需要把信息传给C组件时,B接受A组件的信息,然后利用属性传给C组件,这是一种解决方案,但是如果嵌套的组件过多,会导致代码繁琐,代码维护比较困难;如果C中状态的改变需要传递给A, 使用事件系统一级级往上传递 。
// 父组件demo代码如下 <template> <div> <child-dom :foo="foo" :coo="foo" > </child-dom> </div> </template> <script> import childDom from "./ChildDom.vue"; export default { data() { return { foo:"Hello, world", coo:"Hello,rui" } }, components:{childDom}, } </script> // 子组件child-dom代码如下 <template> <div> <p>foo:{{foo}}</p> </div> </template> <script> export default { name:'child-dom' props:["foo"] } </script>
当显示父组件时,查看Dom结构,结构如下:
$attrs:
inheritAttrs
到 false
,这些默认行为将会被去掉。而通过 (同样是 2.4 新增的) 实例属性 $attrs
可以让这些特性生效,且可以通过 v-bind
显性的绑定到非根元素上。如:// 修改子组件代码如下 <template> <div> <p>foo:{{foo}}</p> <p>attrs:{{$attrs}}</p> <childDomChild v-bind="$attrs"></childDomChild> </div> </template> <script> import childDomChild from './childDomChild'; export default { name:'child-dom' props:["foo"], inheritAttrs:false, } </script> // 新增子组件 childDomChild <template> <div> <p>coo:{{coo}}</p> </div> </template> <script> export default { name:'childDomChild' props:["coo"], inheritAttrs:false } </script>
c组件的信息,怎么同步给a组件呢?
$listeners
包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on=”$listeners” 传入内部组件——在创建更高层次的组件时非常有用。
// A组件代码更新如下 <template> <div> <child-dom :foo="foo" :coo="coo" v-on:upRocket="reciveRocket" > </child-dom> </div> </template> <script> import childDom from "@/components/ChildDom.vue"; export default { name:'demoNo', data() { return { foo:"Hello, world", coo:"Hello,rui" } }, components:{childDom}, methods:{ reciveRocket(){ console.log("reciveRocket success") } } } </script> // b组件更新如下 <template> <div> <p>foo:{{foo}}</p> <p>attrs:{{$attrs}}</p> <childDomChild v-bind="$attrs" v-on="$listeners"></childDomChild> </div> </template> <script> import childDomChild from './childDomChild'; export default { name:'child-dom' props:["foo"], inheritAttrs:false, } </script> // c组件更新如下 <template> <div> <p>coo:{{coo}}</p> <button @click="startUpRocket">我要发射火箭</button> </div> </template> <script> export default { name:'childDomChild', props:['coo'], methods:{ startUpRocket(){ this.$emit("upRocket"); console.log("startUpRocket") } } } </script>
12. tabs存在嵌套时,tab页显示混乱问题
实际上‘手工输入‘和‘文件导入-增量’这两个tab页在子组件deviceWhiteList的子组件中,显示混乱,需要在tabs中添加name属性,并在tabpane中添加响应的tab属性,子组件中的tabs也需要同步添加。如:
13. select传递多个参数
// 方法1: @change="changeClassify($event, 'first')" // 方法2: @change="e => {changeClassify(e, 'first')}"
14. vue-quill-editor 自动获取焦点问题
问题:当editor编辑器在滚动容器底部时,由于会自动获取焦点导致滚动条默认滚动到底部,解决:先禁止获取焦点,获取到内容之后再开启(只这样好像也不起作用,需要将滚动容器顶部的输入框设置为获取焦点)
<el-form-item :prop="'productBase.productName'" label="商品名称:"> <el-input ref="nameRef" class="input-w" :disabled="showDisabled" v-model="filtersData.productBase.productName" placeholder="请输入内容" ></el-input> </el-form-item> this.$refs.nameRef.focus();
vue-quill-editor 在获取到接口传过来的内容之后会获取焦点,所以 思路就是调用接口之前先禁止获取焦点,获取到内容之后再开启。
放到mounted中
this.$refs.myQuillEditor.quill.enable(false);
接口调用之后 并获取到内容之后调用
this.$nextTick(()=>{ this.$refs.myQuillEditor.quill.enable(true); })
15. input设置disabled禁用时,点击无效问题
input标签 disabled属性说明
- 被禁用的input标签 既不可用,也不可进行点击
解决方案
- 使用readonly属性 来替换disabled属性
- 外套一层父标签,给父标签添加点击事件,并设置input的样式为"pointer-events:none" 去掉鼠标事件,然后通过冒泡触发到父标签上的点击事件。
冒泡事件
点击子标签,会一层一层往上传,并触发父标签的绑定事件
取消冒泡事件
e.stopPropagation();