二级路由
welcome子路由页面在home页面当中的写法:
过渡
第一步:
第二步:
动画
用法与过渡效果一样,可以自己写动画,也可以用第三方库animate.css
注:animate.css网站在国内看不了,需要去swiper网站
https://swiper.com.cn/usage/animate/index.html
ref
vue中ref的使用大全
- 基本用法,本页面获取dom元素
<template>
<div id="app">
<div ref="testDom">11111</div>
<button @click="getTest">获取test节点</button>
</div>
</template>
<script>
export default {
methods: {
getTest() {
console.log(this.$refs.testDom)
}
}
};
</script>
获取子组件中的data
子组件
<template>
<div>
{{ msg }}
</div>
</template>
<script>
export default {
data() {
return {
msg: "hello world"
}
}
}
</script>
父组件
<template>
<div id="app">
<HelloWorld ref="hello"/>
<button @click="getHello">获取helloworld组件中的值</button>
</div>
</template>
<script>
import HelloWorld from "./components/HelloWorld.vue";
export default {
components: {
HelloWorld
},
data() {
return {}
},
methods: {
getHello() {
console.log(this.$refs.hello.msg)
}
}
};
</script>
子组件调用父组件方法
子组件
<template>
<div>
</div>
</template>
<script>
export default {
methods: {
open() {
console.log("调用了");
// 调用父组件方法
this.$emit("refreshData",111111);
}
}
}
</script>
父组件
<template>
<div id="app">
<HelloWorld ref="hello" @refreshData="getData"/>
<button @click="getHello">获取helloworld组件中的值</button>
</div>
</template>
<script>
import HelloWorld from "./components/HelloWorld.vue";
export default {
components: {
HelloWorld
},
data() {
return {}
},
methods: {
getHello() {
this.$refs.hello.open()
},
getData() {
console.log('111111')
}
}
};
</script>
mixin
混入 (mixin) 提供了一种非常灵活的方式,来分发 Vue 组件中的可复用功能。一个混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项将被“混合”进入该组件本身的选项。
// 定义一个混入对象
var myMixin = {
created: function () {
this.hello()
},
methods: {
hello: function () {
console.log('hello from mixin!')
}
}
}
// 定义一个使用混入对象的组件
var Component = Vue.extend({
mixins: [myMixin]
})
var component = new Component() // => "hello from mixin!"
vuex
Vuex 允许我们在 store 中定义“getter”(可以认为是 store 的计算属性)。就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。vuex|Getter
const store = new Vuex.Store({
state: {
todos: [
{ id: 1, text: '...', done: true },
{ id: 2, text: '...', done: false }
]
},
getters: {
doneTodos: state => {
return state.todos.filter(todo => todo.done)
}
}
})
Module
const moduleA = {
state: { ... },
mutations: { ... },
actions: { ... },
getters: { ... }
}
const moduleB = {
state: { ... },
mutations: { ... },
actions: { ... }
}
const store = new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
}
})
store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态
命名空间
namespaced:ture
- 默认情况下,模块内部的 action、mutation 和 getter 是注册在全局命名空间的——这样使得多个模块能够对同一 mutation 或 action 作出响应。
- 如果希望你的模块具有更高的封装度和复用性,你可以通过添加 namespaced: true 的方式使其成为带命名空间的模块。当模块被注册后,它的所有 getter、action 及 mutation 都会自动根据模块注册的路径调整命名。
const store = new Vuex.Store({
modules: {
account: {
namespaced: true,
// 模块内容(module assets)
state: { ... }, // 模块内的状态已经是嵌套的了,使用 `namespaced` 属性不会对其产生影响
getters: {
isAdmin () { ... } // -> getters['account/isAdmin']
},
actions: {
login () { ... } // -> dispatch('account/login')
},
mutations: {
login () { ... } // -> commit('account/login')
},
// 嵌套模块
modules: {
// 继承父模块的命名空间
myPage: {
state: { ... },
getters: {
profile () { ... } // -> getters['account/profile']
}
},
// 进一步嵌套命名空间
posts: {
namespaced: true,
state: { ... },
getters: {
popular () { ... } // -> getters['account/posts/popular']
}
}
}
}
}
})
启用了命名空间的 getter 和 action 会收到局部化的 getter,dispatch 和
commit。换言之,你在使用模块内容(module assets)时不需要在同一模块
内额外添加空间名前缀。更改 namespaced 属性后不需要修改模块内的代码。
- vuex中的store分模块管理,需要在store的index.js中引入各个模块,为了解决不同模块命名冲突的问题,将不同模块的namespaced:true,之后在不同页面中引入getter、actions、mutations时,需要加上所属的模块名
vuex-persistedstate插件
Vuex持久化插件-解决刷新数据消失的问题
vuex可以进行全局的状态管理,但刷新后刷新后数据会消失,这是我们不愿意看到的。怎么解决呢,我们可以结合本地存储做到数据持久化,也可以通过插件-vuex-persistedstate。
- 插件的原理其实也是结合了存储方式,只是统一的配置就不需要手动每次都写存储方法。
- 默认存储到localStorage。
使用方法
- 安装
npm install vuex-persistedstate --save
- 引入及配置
在store下的index.js中
import createPersistedState from "vuex-persistedstate"
const store = new Vuex.Store({
// ...
plugins: [createPersistedState()]
})
想要存储到sessionStorage,配置如下
import createPersistedState from "vuex-persistedstate"
const store = new Vuex.Store({
// ...
plugins: [createPersistedState({
storage: window.sessionStorage
})]
})
想使用cookie同理 默认持久化所有state。
import createPersistedState from "vuex-persistedstate"
const store = new Vuex.Store({
// ...
plugins: [createPersistedState({
storage: window.sessionStorage,
reducer(val) {
return {
// 只储存state中的assessmentData
assessmentData: val.assessmentData
}
}
})]
vuex引用多个插件的写法
import createPersistedState from "vuex-persistedstate"
import createLogger from 'vuex/dist/logger'
// 判断环境 vuex提示生产环境中不使用
const debug = process.env.NODE_ENV !== 'production'
const createPersisted = createPersistedState({
storage: window.sessionStorage
})
export default new Vuex.Store({
// ...
plugins: debug ? [createLogger(), createPersisted] : [createPersisted]
})
plugins要是一个一维数组不然会解析错误
父子组件之间的传值
父组件往子组件传值props
①定义父组件,父组件传递 number这个数值给子组件,如果传递的参数很多,推荐使用json数组{}的形式
**父组件相关代码**
<template>
<div class='parent'> //传递数字88给自组件
<children number=88></children>
</div>
</template>
<script>
import Childern from './components/childern' //引入子组件
export default{
components:{
Childern
}
}
</script>
②定义子组件,子组件通过 props方法获取父组件传递过来的值。props中可以定义能接收的数据类型,如果不符合会报错。
**子组件相关代码**
<template>
<div class='children'>
{{number}} //显示父组件传递过来的值 88
</div>
</template>
<script>
export default{
props:[
//限制父组件传递值的类型
'number':[Number,String,Object],
//可以传递多个值用逗号隔开
'string':[String]
]
}
//当然也可以简单一点,如果不考虑数据类型,直接 props:["number","string"]就可以了,中括号包裹,多个值使用,分隔。
</script>
③假如接收的参数 是动态的,比如 input输入的内容 v-model的形式
注意:传递的参数名称 支持驼峰命名,下图 描述不正确(1.0是不支持的)
**父组件相关代码**
<template>
<div class='parent'>
<input type='text' v-model='inputText'>
<!--注意动态绑定属性时不支持驼峰命名,用‘-’隔开-->
<children :input-val='inputText'></children>
</div>
</template>
<script>
import Childern from './components/childern' //引入子组件
export default{
components:{
Childern
}
}
</script>
**子组件相关代码**
<template>
<div class='children'>
{{input-val}}
</div>
</template>
<script>
export default{
props:[
'input-val':[String]
]
}
</script>
④父子组件传值,数据是异步请求,有可能数据渲染时报错
原因:异步请求时,数据还没有获取到但是此时已经渲染节点了
解决方案:可以在 父组件需要传递数据的节点加上 v-if = false,异步请求获取数据后,v-if = true
(2)子组件往父组件传值,通过emit事件
**子组件相关代码**
<template>
<div class='children'>
<!-- 子组件通过触发emit方法将值传递给父组件-->
<button @click='emitToparent'>点击按钮把值传递给父组件</button>
</div>
</template>
<script>
export default{
methods:{
emitToparent(){//不支持驼峰
//child-event子组件中自定义方法
this.$emit('child-event','我是子组件传递给父组件的值')
}
}
}
</script>
**父组件相关代码**
<template>
<div class='parent'>
<!-- 触发了父组件的parentEvent方法,然后执行相应的操作-->
<children @child-event='parentEvent'></children>
</div>
</template>
<script>
import Childern from './components/childern' //引入子组件
export default{
components:{
Childern
},
methods:{
parentEvent(data){//data是从子组件传递过来的值
console.log(data)//打印内容是:我是子组件传递给父组件的值
}
}
}
</script>
(3)不同组件之间传值,通过eventBus(小项目少页面用eventBus,大项目多页面使用 vuex)
①定义一个新的vue实例专门用于传递数据,并导出
eventBus.js文件相关代码
import Vue from 'vue'
exprots default new Vue()
②定义传递的方法名和传输内容,点击事件或钩子函数触发eventBus.emit事件
组件A相关代码
<template>
<div class='componetsA'>
<button @click='emitToB'>点击按钮将数据传递给组件B</button>
</div>
</template>
<script>
import eventBus from './common/eventBus.js' //引入eventBus,实际是引入了另一个vue实例
export default{
methods:{
emitToB(){
eventBus.$emit('eventFromA','我是A组件传递给B组件的值')
}
}
}
</script>
③接收传递过来的数据
注意:enentBus是一个另一个新的Vue实例,区分两个this所代表得vue实例
组件B相关代码
<template>
<div class='componetsB'>
{{title}}//显示A组件传递过来的值
</div>
</template>
<script>
import eventBus from './common/eventBus.js' //引入eventBus,实际是引入了另一个vue实例
export default{
data:{
return{
title:''
}
},+
mounted(){
this.getEventData()//这个是在mounted钩子函数执行获取数据的方法
}
methods:{
getEventData(){
const that = this //this是项目vue实例用that接受,与eventBus的vue实例区分
eventBus.$on('eventFromA',function(val){
that.title = val
})
}
}
}
</script>
六.Vue路由传参的几种方式
vue路由传参是指嵌套路由时父路由向子路由传递参数,否则操作无效。传参方式可以划分为params传参和query传参,params传参又可以分为url中显示参数和不显示参数两种方式。
1.push方法跳转
### 手写完整的path:
this.$router.push({path:`/user/${userId}`})
这样传递参数的话,配置路由的时候需要在path上加参数path:user/:userId。
这种接收参数的方式是this.$route.params.userId。
2.query方式传参和接收参数
传参:
this.$router.push({
path:'/xxx',
query:{
id:id
}
})
接收参数:
this.$route.query.id
注意:传参是this.$router,接收参数是this.$route,这里千万要看清了!!!
this. r o u t e r 和 t h i s . router 和this. router和this.route有何区别?
- 1. r o u t e r 为 V u e R o u t e r 实 例 , 想 要 导 航 到 不 同 U R L , 则 使 用 router为VueRouter实例,想要导航到不同URL,则使用 router为VueRouter实例,想要导航到不同URL,则使用router.push方法
- 2.$route为当前router跳转对象,里面可以获取name、path、query、params等
params方式传参和接收参数
传参:
this.$router.push({
name:'xxx',
params:{
id:id
}
})
接收参数:
this.$route.params.id
注意:params传参,push里面只能是 name:'xxxx',不能是path:'/xxx',因为params只能用name来引入路由,如果这里写成了path,接收参数页面会是undefined!!!
注意:如果提供了path,params将会被忽略,但是query不属于这种情况。。。
如果使用完整路径和query传参,刷新页面时不会造成路由传参的参数丢失。
另外,二者还有点区别,直白的来说query相当于get请求,页面跳转的时候,可以在地址栏看到请求参数,而params相当于post请求,参数不会再地址栏中显示
router-link标签跳转
params传参(url中显示参数)
定义路由:在定义path路由路径时定义参数名和格式,如 path: "/one/login/:num"
//第一步:引用vue和vue-router ,Vue.use(VueRouter)
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
//第二步:引用定义好的路由组件
import ChildOne from '../components/childOne'
import ChildTwo from '../components/childTwo'
import Log from '../components/log.vue'
import Reg from '../components/reg.vue'
//第三步:定义路由(路由对象包含路由名、路径、组件、参数、子路由等),每一个路由映射到一个组件
//第四步:通过new Router来创建router实例
export default new Router({
mode: 'history',
routes: [
{
path: '/one',
name: 'ChildOne',
component: ChildOne,
children:[
{
path:'/one/log/:num',
component:Log,
},
{
path:'/one/reg/:num',
component:Reg,
},
],
},
{
path: '/two',
name: 'ChildTwo',
component: ChildTwo
}
]
})
在父路由组件上使用router-link进行路由导航,传参用的形式向子路由组件传递参数。使用router-view进行子路由页面内容渲染,父路由组件childOne.vue 如下:
<template>
<div style="border:1px solid red;color:red;">
<p>这是父路由childOne对应的组件页面</p>
<p>下面可以点击显示嵌套的子路由 </p>
<router-link to="/one/log/123">显示登录页面</router-link>
<router-link to="/one/reg/002">显示注册页面</router-link>
<router-view></router-view>
</div>
</template>
子路由通过 this.$route.params.num 的形式来获取父路由向子路由传递过来的参数,子路由组件login.vue如下
<template>
<div style="border:1px solid orange;color:orange;">
<p>登录界面:这是另一个嵌套路由的内容</p>
<h3>{{this.$route.params.num}}</h3>
</div>
</template>
params传参(url中不显示参数)
定义路由时添加name属性给映射的路径取一个别名,router>index.js文件修改router如下
export default new Router({
mode: 'history',
routes: [
{
path: '/one',
name: 'ChildOne',
component: ChildOne,
children:[
{
path:'/one/log/',
name:'Log',
component:Log,
},
{
path:'/one/reg/',
name:'Reg',
component:Reg,
},
],
},
{
path: '/two',
name: 'ChildTwo',
component: ChildTwo
}
]
})
**在父路由组件上使用router-link进行路由导航,使用 <router-link :to="{name:‘home’,params:{id:001}}> 形式传递参数。**注意 ': to= ’ 前面的冒号,childOne.vue组件修改如下:
<template>
<div style="border:1px solid red;color:red;">
<p>这是父路由childOne对应的组件页面</p>
<p>下面可以点击显示嵌套的子路由 </p>
<router-link :to="{name:'Log',params:{num:666}}">显示登录页面</router-link>
<router-link :to="{name:'Reg',params:{num:888}}">显示注册页面</router-link>
<router-view></router-view>
</div>
</template>
子路由组件页面获取父路由传参方式不变,reg.vue 文件如下
<template>
<div style="border:1px solid orange;color:orange;">
<p>登录界面:这是另一个嵌套路由的内容</p>
<h3>子路由获取的参数:{{this.$route.params.num}}</h3>
</div>
</template>
常用的mode模式有两种:
-
默认为hash模式,最明显的标志是,URL上有#号 localhost:8080/#/,路由会监听#后面的信息变化进行路由匹配
-
另一种为history模式,不会有#出现,很大程度上对URL进行了美化。需要注意history模式在打包后的路由跳转需要服务器配合
组件之间eventBus传递数据
//a组件给b组件传数据
**A组件**
<template>
<div class='container'>my-coma---<button @click="fn">给B数据</button></div>
</template>
<script>
import eventBus from '@/eventBus'
export default {
name: 'my-coma',
data () {
return {
aData: 'A组件的数据'
}
},
methods: {
fn () {
eventBus.$emit('fromA', this.aData)
}
}
}
</script>
**b组件**
<template>
<div class='container'>my-comb---{{bData}}</div>
</template>
<script>
import eventBus from '@/eventBus'
export default {
name: 'my-comb',
data () {
return {
bData: ''
}
},
created () {
// 绑定事件 接受A的数据
eventBus.$on('fromA', (data) => {
this.bData = data
})
}
}
</script>
vue渲染组件的过程
Vue 推荐在绝大多数情况下使用 template 来创建你的 HTML。但是模板毕竟是模板,不是真实的dom节点。从模板到真实dom节点还需要经过一些步骤
1.把模板编译为render函数
2.实例进行挂载, 根据根节点render函数的调用,递归的生成虚拟dom
3.对比虚拟dom,渲染到真实dom
4.组件内部data发生变化,组件和子组件引用data作为props重新调用render函数,生成虚拟dom, 返回到步骤3
data变化——通过数据劫持Object.defineProperty(Vue3.0放弃了数据劫持,采用了Proxy观察者)——更新data——编译——更新虚拟DOM——重新渲染
报错
vue.runtime.esm.js?2b0e:619 [Vue warn]: Do not use built-in or reserved HTML elements as component id: table
- 不能使用内置或者保留的HTML或者组件作为组件id
意思就是你使用的组件名的名称跟系统(vue)的内置属性名冲突了,所以创建失败,最好的方法就是换个名字。
Property or method “tableRowcellStyle” is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property.
- 属性或方法“ tableRowcellStyle”未在实例上定义,但在渲染期间被引用。 通过初始化属性,确保该属性在data,props或methods选项中或对于基于类的组件都是反应性的
原因:在data中没有定义一个tableRowcellStyle, 致错
解决方法:在data中定义一个tableRowcellStyle=" "
undefined 表示未声明或已声明但未赋值的变量或不存在的对象,函数没有返回值时,默认返回 undefined,判断是不是 undefined 使用 typeof 函数,其返回的字符串包括如下:
“number”,“string”,“boolean”,“object”,“function”,“undefined”
「Vue 不允许动态添加根级响应式属性,所以你必须在初始化实例前声明根级响应式属性,哪怕只是一个空值」,ok,到这里,问题就解决了,既然说了可以声明空值,那就声明一个空值!
解决:
data() {
return{
tableRowcellStyle: ''
}
}
- 无效的道具:道具“ cellStyle”的类型检查失败。 预期的对象,函数的字符串值为“”
Invalid prop: type check failed for prop “”. Expected Object, Function, got String with value “”.
解决:cellStyle值的类型错误;
- Vue只允许模板里存在一个根节点。
Vue Error: The template root requires exactly one element.
解决:
在 <template> 中添加一个 <div>标签,
之后所有的组件全部加在 <div>即可解决。
- Failed to mount component: template or render function not defined.
翻译:无法安装组件:模板或渲染功能未定义。
vue- Invalid component name: “[object HTMLDivElement]”. Component names should conform to valid custom element name in html5 specification.
翻译:vue-无效的组件名称:“ [object HTMLDivElement]”。 组件名称应符合html5规范中的有效自定义元素名称。
- component is not defined
问题原因:使用公共组件大写了
解决:
<tkTabs/>
修改为
<tk-tabs>
-
Failed to mount component: template or render function not defined
翻译:无法安装组件:模板或渲染功能未定义 -
格式缩进
alt+shift+F